Jump to content

User:It's moon/Sandbox/ReHaunt-form.js

From mediawiki.org

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*	ReHaunt, multi reverse image search tool
	Copyright (C) 2025, 2026  Fumple and It's moon

	This file is part of ReHaunt.

	ReHaunt is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	ReHaunt is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with ReHaunt. If not, see <https://www.gnu.org/licenses/>.	*/

$(function () {
	// Import dependencies (Temporary until converted to Gadget)
	mw.loader.using([
		'mediawiki.api',
		'oojs-ui-core',
		'oojs-ui.styles.icons-alerts',
		'oojs-ui-widgets'
	]).then(function () {

	// Assign variables to URL query parameters - This has some problems at handling file names with & and ? characters, might need REGEX or a different approach.
	const urlQuery = Object.fromEntries(
		new URLSearchParams(location.search)
	);

	// Create the search box and button
	const inputWidget = new OO.ui.SearchInputWidget({
		placeholder: "Search by image URL or file name",
		// Use URL query parameters to autofill the form - this currently doesn't accept filenames that contain ? or & symbols
		value: (urlQuery.search && urlQuery.page) ? urlQuery.search + '?page=' + urlQuery.page : (urlQuery.search || '')
	});
	const buttonWidget = new OO.ui.ButtonWidget({
		label: "Search"
	});
	inputWidget.$element.find('input').add(buttonWidget.$element.find('a')).css({ height: "35px" });

	const actionFieldLayout = new OO.ui.ActionFieldLayout(inputWidget, buttonWidget);

	// Create a container for the output
	const $outputWidget = $("<p>");

	// Replace the loading icon with the search box, button and output container
	$(".user-itsmoon-rehaunt").replaceWith(
		$("<p>").append(actionFieldLayout.$element, $outputWidget)
	);

	// Button and enter key clicks
	buttonWidget.on("click", performSearch);
	inputWidget.on("enter", performSearch);

	// Perform the search
	function performSearch() {
		const input = inputWidget.getValue().trim();
		$outputWidget.empty();
		$('.img-test').empty(); // Part of image thumbnail test down below

		// Check if user input is empty
		if (!input) return;

		// Disable the search box and button
		inputWidget.setDisabled(true);
		buttonWidget.setDisabled(true);

		if (/^https?:\/\//.test(input)) { // I was thinking of replacing this check by a GET request... maybe not, but I'd like to support URLs that start with // (no http/https)... we'll see. GOOD NEWS! There *can* be pages in the File: namespace with / in the title, but they can't be files eg. [[File:Schmettau Plan de la ville de Berlin (georeferenced).jpg/overlay.kml]] which means I can support // urls without a get request (tool needs to add http/https).
			// Encode input URL
			const encodedInput = encodeURIComponent(input);
			renderOutput(encodedInput);
		} else {
			// Separate the filename from input query parameters
			const [ inputFileRaw ] = input.split(/[?&](?=[^?&=\s]+=(?!\.))/);
			// Remove namespace from filename
			const inputFile = inputFileRaw.split(':').pop(); // Needs to be fixed for files that have : in the filename eg. [[File:Wiki Academy 2011 - Wiki Technology: Uses in Education and Business.ogv]]
			// Assign variables to input query parameters
			const inputQuery = Object.fromEntries(
				new URLSearchParams(input.slice(inputFileRaw.length + 1))
			);

			new mw.Api().get({
				action: 'query',
				titles: 'File:' + inputFile,
				prop: 'imageinfo',
				iiprop: 'url',
				iiurlwidth: 800,
				iipage: inputQuery.page,
				redirects: 1
			}).then(function({query}) {
				Object.values(query.pages).forEach(function({imageinfo}) {
					const encodedInput = imageinfo ? encodeURIComponent(imageinfo[0].thumburl) : undefined;
					renderOutput(encodedInput);
				});
			});
		}

		// Generate output
		function renderOutput(encodedInput) {

			// Image thumbnail test, to develop this further use [gerrit.wikimedia.org/g/mediawiki/core/+/HEAD/resources/src/mediawiki.special.upload] as a reference. If really needed do research on who wrote the code and ask them further.
			$('.img-test').append($('<img>',{src:decodeURIComponent(encodedInput), width: '300px'}));

			$outputWidget.append(
				encodedInput
				? new OO.ui.LabelWidget({
					label: new OO.ui.HtmlSnippet(
						'<a href="https://lens.google.com/uploadbyurl?url=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">Google&nbsp;Lens</a>' +
						' •&nbsp;<a href="https://www.google.com/searchbyimage?client=app&image_url=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">Google&nbsp;Classic</a>' +
						' •&nbsp;<a href="https://www.bing.com/images/searchbyimage?cbir=sbi&imgurl=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">Bing</a>' +
						' •&nbsp;<a href="https://yandex.com/images/search?rpt=imageview&url=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">Yandex</a>' +
						' •&nbsp;<a href="https://graph.baidu.com/details?isfromtusoupc=1&carousel=0&extUiData[isLogoShow]=1&image=' + encodedInput + '&promotion_name=" target="_blank" rel="noopener noreferrer" class="external text">Baidu</a>' +
						' •&nbsp;<a href="https://www.tineye.com/search?url=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">TinEye</a>'
						/* Currently not working:
						' •&nbsp;<a href="https://karmadecay.com/search?q=' + encodedInput + '" target="_blank" rel="noopener noreferrer" class="external text">Karma&nbsp;Decay</a>' */
					)
				}).$element.css({ textWrap: "balance" })

				: new OO.ui.MessageWidget({
					type: "error",
					label: "Please enter a valid URL or file name.",
					inline: true
				}).$element.attr("id", "rehaunt-error").css({ display: "inline-block", textAlign: "left" })
			);

			// Reenable the search box and button
			inputWidget.setDisabled(false);
			buttonWidget.setDisabled(false);
		}
	}
	
	// Inkipedia only styling, not needed for other wikis, to be moved to TemplateStyles on that wiki.
	const style = document.createElement("style");
	style.textContent = `
		#rehaunt-error .oo-ui-iconElement-icon {filter:none;}
		#rehaunt-error .oo-ui-iconElement-icon::before {background:#d33;}
		#rehaunt-error .oo-ui-labelElement-label {color:#d33;}
	`;
	document.head.appendChild(style);
	// End of Inkipedia only styling
}); // Import dependencies closing tag (Temporary until converted to Gadget)
});