MediaWiki:Gadget-extsearch.js

Frå Wikipedia – det frie oppslagsverket

Merk: Etter lagring vil det kanskje vera naudsynt at nettlesaren slettar mellomlageret sitt for at endringane skal tre i kraft.

  • Firefox og Safari: Haldt nede Shift medan du klikkar på Oppdater, eller trykk anten Ctrl-F5 eller Ctrl-R (⌘-R på Mac)
  • Google Chrome: Trykk Ctrl-Shift-R (⌘-Shift-R på Mac)
  • Internet Explorer og Edge: Haldt nede Ctrl medan du klikkar Oppdater, eller trykk Ctrl-F5
  • Opera: Tøm mellomlageret i Verktøy → Innstillingar
/**
 * External search gadget
 * 
 * This gadget adds links to various external databases/websites in the sidebar
 * for articles. By default, it searches for the page title, but if the user
 * has selected some text in the article, it will search for that text instead.
 * 
 * Idea from several older gadgets on no.wikipedia.org by User:Jeblad.
 * 
 * @author Jon Harald Søby
 * @version 1.2.1 (2024-03-21)
 */

( function() {
	if ( !mw.config.get( 'wgIsArticle' ) ) {
		return;
	}

	const conf = JSON.parse( mw.user.options.get( 'userjs-extsearch' ) ) || [];

	//---- START gadget configuration ----//
	// Search engine data. The key is the ID for the search engine (which will
	// also be used when saving the options), and the value is an object with
	// three keys:
	// * 'name': The displayed name of the search engine
	// * 'title': What will be shown when users hover the search engine
	// * 'urlPattern': The URL to form a search in the search engine, with $1
	//                 where the actual search term goes.
	// TODO: Let users customize their own search engines?
	const searchEngineData = {
		'bokhylla': {
			'name': 'Nettbiblioteket (tidl. Bokhylla)',
			'title': 'Søk etter «$1» i Nettbiblioteket hos Nasjonalbiblioteket',
			'urlPattern': 'https://www.nb.no/search?q=$1'
		},
		'twl': {
			'name': 'The Wikipedia Library',
			'title': 'Søk etter «$1» i The Wikipedia Library',
			'urlPattern': 'https://wikipedialibrary.wmflabs.org/search/?q=$1'
		},
		'oria': {
			'name': 'Oria (tidl. Bibsys)',
			'title': 'Søk etter «$1» i Oria',
			'urlPattern': 'https://bibsys-almaprimo.hosted.exlibrisgroup.com/primo-explore/search?vid=BIBSYS&query=any,contains,$1'
		},
		'snl': {
			'name': 'Store norske leksikon',
			'title': 'Søk etter «$1» i Store norske leksikon',
			'urlPattern': 'https://snl.no/.search?query=$1'
		},
		'oslobilder': {
			'name': 'Oslobilder',
			'title': 'Søk etter «$1» i Oslobilder',
			'urlPattern': 'http://oslobilder.no/search?searchstring=$1'
		},
		'digitaltmuseum': {
			'name': 'DigitaltMuseum',
			'title': 'Søk etter «$1» i DigitaltMuseum',
			'urlPattern': 'https://digitaltmuseum.no/search/?q=$1'
		},
		'kulturminnesok': {
			'name': 'Kulturminnesøk',
			'title': 'Søk etter «$1» hos Kulturminnesøk',
			'urlPattern': 'https://kulturminnesok.no/kart/?q=$1'
		},
		'ssr': {
			'name': 'SSR faktaark',
			'title': 'Søk etter «$1» i Sentralt stedsnavnregister',
			'urlPattern': 'https://stadnamn.kartverket.no/fakta/?stedsnavn=$1'
		},
		'google': {
			'name': 'Google',
			'title': 'Søk etter «$1» med Google',
			'urlPattern': 'https://www.google.com/search?q=$1'
		},
		'bing': {
			'name': 'Bing',
			'title': 'Søk etter «$1» med Bing',
			'urlPattern': 'https://www.bing.com/search?q=$1'
		},
		'duckduckgo': {
			'name': 'DuckDuckGo',
			'title': 'Søk etter «$1» med DuckDuckGo',
			'urlPattern': 'https://duckduckgo.com/?q=$1'
		},
		'nowiki': {
			'name': 'Wikipedia på bokmål',
			'title': 'Søk etter «$1» i Wikipedia på bokmål',
			'urlPattern': 'https://no.wikipedia.org/w/index.php?fulltext=1&search=$1'
		},
		'nnwiki': {
			'name': 'Wikipedia på nynorsk',
			'title': 'Søk etter «$1» i Wikipedia på nynorsk',
			'urlPattern': 'https://nn.wikipedia.org/w/index.php?fulltext=1&search=$1'
		},
		'enwiki': {
			'name': 'Wikipedia på engelsk',
			'title': 'Søk etter «$1» i Wikipedia på engelsk',
			'urlPattern': 'https://en.wikipedia.org/w/index.php?fulltext=1&search=$1'
		},
		'svwiki': {
			'name': 'Wikipedia på svensk',
			'title': 'Søk etter «$1» i Wikipedia på svensk',
			'urlPattern': 'https://sv.wikipedia.org/w/index.php?fulltext=1&search=$1'
		},
		'wikidata': {
			'name': 'Wikidata',
			'title': 'Søk etter «$1» på Wikidata',
			'urlPattern': 'https://www.wikidata.org/w/index.php?search=$1'
		}
	};

	const messages = {
		'userjs-extsearch-portlet-title': 'Eksterne søk',
		'userjs-extsearch-title': 'Endra søkemotorar',
		'userjs-extsearch-save': 'Lagra',
		'userjs-extsearch-cancel': 'Avbryt',
		'userjs-extsearch-error': 'Noko gjekk gale. Last sida på nytt og prøv igjen.',
		'userjs-extsearch-suggestions': 'Forslag til fleire søkemotorar? [[MediaWiki-diskusjon:Gadget-extsearch.js|Legg dei inn her]].',
	};
	mw.messages.set( messages );
	//---- END gadget configuration ----//

	const portletId = 'p-query';
	mw.util.addPortlet( portletId, mw.msg( 'userjs-extsearch-portlet-title' ), '#p-Wikipedia' ); // Temporary, as workaround for T351029
	mw.util.showPortlet( portletId );

	function addSearchLinks() {
		for ( const engine of conf ) {
			if ( !searchEngineData[ engine ] ) {
				// Check if the search engine is in the current list of search
				// engines (defined above). If we don't do this, an error
				// could occur if a search engine the user had enabled
				// was removed from the gadget.
				continue;
			}

			mw.util.addPortletLink(
				portletId,
				searchEngineData[ engine ].urlPattern.replace(
					'$1',
					encodeURIComponent( mw.config.get( 'wgTitle' ) )
				),
				searchEngineData[ engine ].name,
				'extsearch-' + engine,
				searchEngineData[ engine ].title
			);
		}

		// Functionality to search for the selected text
		$( '#' + portletId + ' a' ).on( 'mouseenter focus', function( e ) {
			const thisEngine = $( this ).closest( 'li' ).attr( 'id' ).split( '-' );

			if ( thisEngine[ 0 ] !== 'extsearch' ) {
				// Just in case an unrelated link snuck in there
				return;
			}

			let selection = window.getSelection().toString() || mw.config.get( 'wgTitle' );
			// Truncate the selection to max 128 characters (in case the user
			// has selected an entire paragraph, for instance).
			selection = selection.substring( 0, 128 );
			const url = searchEngineData[ thisEngine[ 1 ] ].urlPattern.replace(
					'$1',
					encodeURIComponent( selection )
				);
			$( this )
				.attr( 'href', url )
				.attr( 'title', searchEngineData[ thisEngine[ 1 ] ].title.replace( '$1', selection ) );
		} );
	}

	function addOptionsButton() {
		mw.util.addCSS(
			'body.skin-vector .extsearch-options-button,\n' +
			'body.skin-vector-2022 .extsearch-options-button {\n\t' +
				'margin-top: 8px;\n' +
			'}\n' +
			'.extsearch-options-button {\n\t' +
				'background: transparent url(' +
					'/w/extensions/UniversalLanguageSelector/resources/images/cog-sprite.svg' +
					') no-repeat center top;\n\t' +
				'border: 0;\n\t' +
				'min-height: 16px;\n\t' +
				'min-width: 16px;\n\t' +
				'cursor: pointer;\n\t' +
				'float: right\n\t' +
			'}\n' +
				'.extsearch-options-button:hover {\n\t' +
				'background-position: center -16px;\n' +
			'}'
		);
		const $optionsButton = $( '<button>' )
				.addClass( 'extsearch-options-button' )
				.attr( 'title', mw.msg( 'userjs-extsearch-title' ) )
				.on( 'click', optionsDialog );
		$( '#' + portletId ).prepend( $optionsButton );
	}

	function setOptions( options ) {
		return new mw.Api().postWithEditToken( {
			action: 'options',
			format: 'json',
			formatversion: 2,
			optionname: 'userjs-extsearch',
			optionvalue: JSON.stringify( options )
		} );
	}

	function optionsDialog() {
		function EditSearchDialog( config ) {
			EditSearchDialog.super.call( this, config );
		}
		OO.inheritClass( EditSearchDialog, OO.ui.ProcessDialog );

		EditSearchDialog.static.name = 'editSearchDialog';
		EditSearchDialog.static.title = mw.msg( 'userjs-extsearch-title' );
		EditSearchDialog.static.actions = [
			{
				action: 'save',
				label: mw.msg( 'userjs-extsearch-save' ),
				flags: [ 'primary', 'progressive' ],
				framed: true
			},
			{
				label: mw.msg( 'userjs-extsearch-cancel' ),
				flags: 'safe',
				icon: 'close',
				invisibleLabel: true
			}
		];

		EditSearchDialog.prototype.initialize = function() {
			EditSearchDialog.super.prototype.initialize.apply( this, arguments );

			this.fields = [];
			const fieldset = new OO.ui.FieldsetLayout();
			for ( const key in searchEngineData ) {
				const checkbox = new OO.ui.CheckboxInputWidget( {
					value: key,
					selected: conf.includes( key )
				} );
				this.fields.push( checkbox );
				fieldset.addItems( [
					new OO.ui.FieldLayout( checkbox, {
						label: searchEngineData[ key ].name,
						align: 'inline'
					} )
				] );
			}

			this.content = new OO.ui.PanelLayout(
				{
					padded: true,
					expanded: false,
					$content: fieldset.$element.css( { 'column-count': 2 } )
				}
			);
			this.content.$element.append( $( '<hr>' ) );
			this.content.$element.append( $( '<p>' ).append(
				mw.message( 'userjs-extsearch-suggestions' ).parseDom()
			) );
			this.$body.append( this.content.$element );
		};
		
		EditSearchDialog.prototype.getActionProcess = function( action ) {
			const dialog = this;
			if ( action === 'save' ) {
				dialog.pushPending();
				const newoptions = [];
				for ( const field of dialog.fields ) {
					if ( field.selected ) {
						newoptions.push( field.value );
					}
				}
				setOptions( newoptions ).then( function() {
					dialog.close();
					location.reload();
				} ).fail( function() {
					mw.notify( mw.msg( 'userjs-extsearch-error', { type: 'error' } ) );
				} );
			}
			return EditSearchDialog.super.prototype.getActionProcess.call( this, action );
		};

		const windowManager = new OO.ui.WindowManager();
		$( document.body ).append( windowManager.$element );

		const dialog = new EditSearchDialog();
		windowManager.addWindows( [ dialog ] );
		windowManager.openWindow( dialog );
	}

	// Add the options cog to the sidebar portlet
	addOptionsButton();
	// Add the search links to the sidebar portlet
	addSearchLinks();
} )();