User:The Transhumanist/SearchSuite.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <syntaxhighlight lang="javascript">

/* 
Installation instructions and the user's manual are located on this script's 
talk page.

SearchSuite.js: this script provides menu items for the following suite of 
search features:

	Hide/show details 
	Hide/show sister results
	Hide/show redirecteds/category-members	
	Sort/unsort 
	Show/hide list item wiki formatting, like this: * [[]] 
		(For easy copying/pasting)
		
The script also includes TrueMatch, a work-around for the "intitle:" bug.

This script was forked on 2018/02/13 from [[User:The_Transhumanist/StripSearchSorted.js]] (see:
https://en.wikipedia.org/w/index.php?title=User:The_Transhumanist/StripSearchSorted.js&oldid=821420973 ),
which was in turn derived from [[User:The Transhumanist/OutlineViewAnnotationToggler.js]]:
https://en.wikipedia.org/w/index.php?title=User:The_Transhumanist/OutlineViewAnnotationToggler.js&oldid=807301505

Brief comments are provided within the sourcecode below. For extensive explanatory 
notes on what the source code does and how it works, see the Script's workshop on 
the talk page. (Only partially done so far).

*/

// ============== Set up ==============

// Start off with a bodyguard function to reserve the aliases mw and $ within 
// the scope of this function, so that we can rely on them meaning "mediaWiki" 
// and "jQuery", respectively: these alias definitions follow this function's 
// closing curly bracket at the end of the script.

( function ( mw, $ ) {

	// ============== Load dependencies ============== 
	// For support of mw.util.addPortletLink
	mw.loader.using( ['mediawiki.util'], function () {
	
    // ============== ready() event listener/handler ==============
    // below is jQuery short-hand for $(document).ready(function() { ... });
    // it makes the rest of the script wait until the page's DOM is loaded and ready
    $(function() {
        
		// ============== deactivation filters (guard clauses) ==============
        // End the script if " - Search results - Wikipedia" is not in the page title
		if (document.title.indexOf(" - Search results - Wikipedia") == -1) {
			// use a return statement to end the local function and hence the program's body
			// important: this approach does not work outside of a function
			return;
		}

		// End of set up
			
   	    // =================== Prep work =====================
				
		// Variable declarations, etc., go here

        var SRSisterSwitch; 
		var SRRedirectedSwitch;
        var SRDetailSwitch; 
        var SRWikifySwitch; 
        var SRSortSwitch; 
		var elems0;
		var elems1;
		var elems2;

        // get the values of our status variables from memory
        // (this tells us what modes to start in)
        // "SR" stands for "search results"
        var SRSisterStatus = localStorage.getItem('SRSisterStatus');
        var SRRedirectedStatus = localStorage.getItem('SRRedirectedStatus');
        var SRDetailStatus = localStorage.getItem('SRDetailStatus');
        var SRWikifyStatus = localStorage.getItem('SRWikifyStatus');
        var SRSortStatus = localStorage.getItem('SRSortStatus');

       	// ================== Core program =================== 

		// True match = make "intitle:" work the way it is supposed to
		// Call TrueMatch function - see in Subroutines at program's end
		TrueMatch();


		// Tag each entry with numerical ids, to record their original order.
		elems0 = $('.mw-search-results li').detach().attr( "id", function( arr ) {
			return arr; 
		}); 
		$('.mw-search-results').append(elems0);


		// Wrap mw-search-result-heading's <a> element in wikilink delimiters
		// which are in turn wrapped in classed span tags. Prefix with li asterisk.
		
		// Wrap step 1: Make the first link in entry targettable by adding a class to them
		$( ".mw-search-result-heading" ).children( "a" ).addClass( "sr-a" );

      	// Wrap step 2: wrap those page links in classed list item wikiformatting (* [[]]), 
      	// with the class called "wikibrackets"
		var searchResultAElements = document.getElementsByClassName("sr-a");
		var i;
		for (i = 0; i < searchResultAElements.length; i++) {
		    searchResultAElements[i].outerHTML = searchResultAElements[i].outerHTML.replace(/(<a.*?(<\/a>))/g,'<span class=wikibrackets>* [[</span>$1<span class=wikibrackets>]]</span>');
		}

		// run the appropriate wikify function based on stored status
		// SRWikify must be first, so that the wrapping link wikicodes (above) don't show by default
		SRWikify();
		// run the appropriate sort function based on stored status				
		SRSort();
		// run the appropriate sisters function based on stored status 
		SRSister();
		// run the appropriate redirecteds function based on stored status 
		SRRedirected();
		// run the appropriate detail function based on stored status 
		SRDetail();
		// run the filter function for honing down search results
		if(typeof SRFilter !== 'undefined') SRFilter();

        // ======================== Subroutines ===========================
        // Functions (aka subroutines) are activated only when they are called.
        // Below are the functions called in the core of the program above.
        // They are placed here at the end of the program, so that the script's 
        // flow is easier to follow.

		// ============ TrueMatch function to make intitle work right =============
		function TrueMatch() {
			// The purpose of this function is to make the intitle search
			// feature actually work, by filtering out the entries that do
			// not contain the intitle search string in their titles.

	        // Activation filter:
	        // Run this function only if 'intitle:"' is in the page title
	        // Notice the lone " after intitle:
			if (document.title.indexOf('intitle:"') != -1) {

				// Body of function
				// Create variable with page title
				var docTitle = document.title;

				// Extract the intitle search string from the page title
				// We want the part between the quotation marks
				// var regexIntitle = new RegExp('intitle:"(.+?)(")(.*)','i'); //case-insensitve version
				var regexIntitle = new RegExp('.*?intitle:"(.+?)(")(.*)');
				var intitle;
				intitle = docTitle.replace(regexIntitle,"$1");

				// Filter out search results that do not match the intitle search string
				// First, mark true results with a class
				$('.mw-search-results').find('li').has( 'div > a:contains("' + intitle + '")' ).addClass('truematch');
				// Now remove the other results
				$('.mw-search-results').find('li').not('.truematch').remove();
			}
		}
		
        // =============== Function for Wikify control contruct =================
		function SRWikify() {
			// run the appropriate wikify function based on stored status
			// starts "off" by default, if no status is stored (i.e., when script is used
			// for the first time)
	        if ( SRWikifyStatus === "on" ) {
            SRWikifyOn();
	        } else {
	            SRWikifyOff();
	        }
		}	

        // =============== Function for Sort control contruct =================
		function SRSort() {
			// run the appropriate sort function based on stored status				
			// starts "off" by default, if no status is stored (i.e., when script is used
			// for the first time)
	        if ( SRSortStatus === "on" ) {
	            SRSortOn();
	        } else {
	            SRSortOff();
	        }
		}	

        // =============== Function for Sister control contruct =================
		function SRSister() {
			// run the appropriate sisters function based on stored status 
			// starts "on" by default, if no status is stored (i.e., when script is used
			// for the first time)
	        if ( SRSisterStatus === "off" ) {
	            SRSisterOff();
	        } else {
	            SRSisterOn();
	        }
		}	

        // =============== Function for Redirected control contruct =================
		function SRRedirected() {
			// run the appropriate redirecteds function based on stored status 
			// starts "on" by default, if no status is stored (i.e., when script is used
			// for the first time)
	        if ( SRRedirectedStatus === "off" ) {
	            SRRedirectedOff();
	        } else {
	            SRRedirectedOn();
	        }
		}	

        // =============== Function for Detail control contruct =================
		function SRDetail() {
			// run the appropriate detail function based on stored status 
			// starts "on" by default, if no status is stored (i.e., when script is used
			// for the first time)
	        if ( SRDetailStatus === "off" ) {
	            SRDetailOff();
	        } else {
	            SRDetailOn();
	        }
		}	

        // ============ Function to hide sister search results (turn sister off) ==============
        function SRSisterOff() {
            // store status so it persists across page loads
            localStorage.setItem("SRSisterStatus", "off");

            // Hide interwiki results (per http://api.jquery.com/hide)
            $('#mw-interwiki-results').hide();

            // now we have to update the menu item 
            // (referred to in this script as "SRSisterSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRSisterSwitch ) {
                SRSisterSwitch.parentNode.removeChild(SRSisterSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRSisterSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR sisters \(turn on\)', 'tb-Sister', 'Show sister projects search results', '', '#tb-Sort' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRSisterSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                SRSisterOn();
            } );
        }

        // ============ Function to show sister search results (turn sister on) ==============
        function SRSisterOn() {
            // store status so it persists across page loads
            localStorage.setItem("SRSisterStatus", "on");

            // Show interwiki results (per http://api.jquery.com/show)
            $('#mw-interwiki-results').show();

            // now we have to update the menu item 
            // (referred to in this script as "SRSisterSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRSisterSwitch ) {
                SRSisterSwitch.parentNode.removeChild(SRSisterSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRSisterSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR sisters \(turn off\)', 'tb-Sister', 'Hide sister projects search results', '', '#tb-Sort' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRSisterSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                SRSisterOff();
            } );
        }
            
        // ====== Function to hide redirecteds and category-based search results (turn redirected off) ======
        function SRRedirectedOff() {
            // store status so it persists across page loads
            localStorage.setItem("SRRedirectedStatus", "off");

            // Hide redirected entries, and entries from matching categories (per http://api.jquery.com/hide)
			$("li").has(".searchalttitle").hide();

            // now we have to update the menu item 
            // (referred to in this script as "SRRedirectedSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRRedirectedSwitch ) {
                SRRedirectedSwitch.parentNode.removeChild(SRRedirectedSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRRedirectedSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR redirecteds \(turn on\)', 'tb-Redirecteds', 'Show redirected entries and entries from matching categories', '', '#tb-Sister' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRRedirectedSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                SRRedirectedOn();
            } );
        }

        // ====== Function to show redirecteds and category-based search results (turn redirected on) ======
        function SRRedirectedOn() {
            // store status so it persists across page loads
            localStorage.setItem("SRRedirectedStatus", "on");

            // Show redirected entries, and entries from matching categories (per http://api.jquery.com/hide)
			$("li").has(".searchalttitle").show();

            // now we have to update the menu item 
            // (referred to in this script as "SRRedirectedSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRRedirectedSwitch ) {
                SRRedirectedSwitch.parentNode.removeChild(SRRedirectedSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRRedirectedSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR redirecteds \(turn off\)', 'tb-Redirecteds', 'Hide redirected entries and entries from matching categories', '', '#tb-Sister' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRRedirectedSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                SRRedirectedOff();
            } );
        }
            
        // ============ Function to hide details (turn detail off) ==============
        function SRDetailOff() {
            // store status so it persists across page loads
            localStorage.setItem("SRDetailStatus", "off");

            // Hide details (per http://api.jquery.com/hide)
            $('.searchresult').hide();
            $('.mw-search-result-data').hide();

			// Remove extra whitespace by changing element from a block to inline
			$( ".mw-search-results" ).find( 'li' ).css( 'display', 'inline' );

			// Accessing children elements above may be affecting display of list items with .searchalttitle
			// Therefore, make sure redirecteds remain hidden or shown based on their status:
	        if ( SRRedirectedStatus === "off" ) {
				$("li").has(".searchalttitle").hide();
	        } else {
	        	$("li").has(".searchalttitle").show();
	        }

            // now we have to update the menu item 
            // (referred to in this script as "SRDetailSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRDetailSwitch ) {
                SRDetailSwitch.parentNode.removeChild(SRDetailSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRDetailSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR details \(turn on\)', 'tb-Detail', 'Show the details', '', '#tb-Redirecteds' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRDetailSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                    SRDetailOn();
            } );
        }

        // ============ Function to show details (turn detail on) ==============
        function SRDetailOn() {
            // store status so it persists across page loads
            localStorage.setItem("SRDetailStatus", "on");

            // Show details (per http://api.jquery.com/show)
            $('.searchresult').show();
            $('.mw-search-result-data').show();

			// Add the original extra whitespace back in by changing element from inline to a block
			$( ".mw-search-results" ).find( 'li' ).css( 'display', 'block' );

			// Accessing children elements above may be affecting display of list items with .searchalttitle
			// Therefore, make sure redirecteds remain hidden or shown based on their status:
	        if ( SRRedirectedStatus === "off" ) {
				$("li").has(".searchalttitle").hide();
	        } else {
	        	$("li").has(".searchalttitle").show();
	        }

            // now we have to update the menu item 
            // (referred to in this script as "SRDetailSwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRDetailSwitch ) {
                SRDetailSwitch.parentNode.removeChild(SRDetailSwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRDetailSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR details \(turn off\)', 'tb-Detail', 'Hide the details', '', '#tb-Redirecteds' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRDetailSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                    SRDetailOff();
            } );
        }
            
        // ============ Function to hide wikibrackets (turn wikify off) ==============
        function SRWikifyOff() {
            // store status so it persists across page loads
            localStorage.setItem("SRWikifyStatus", "off");

            // Hide wikibrackets (per http://api.jquery.com/hide)
            $('.wikibrackets').hide();

            // now we have to update the menu item 
            // (referred to in this script as "SRWikifySwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRWikifySwitch ) {
                SRWikifySwitch.parentNode.removeChild(SRWikifySwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRWikifySwitch = mw.util.addPortletLink( 'p-tb', '', 'SR wikify \(turn on\)', 'tb-Wikify', 'Show wiki brackets around links for easy copy and paste', '', '#t-specialpages' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRWikifySwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                SRWikifyOn();
            } );
        }

        // ============ Function to show wikibrackets (turn wikify on) ==============
        function SRWikifyOn() {
            // store status so it persists across page loads
            localStorage.setItem("SRWikifyStatus", "on");

            // Show interwiki results (per http://api.jquery.com/show)
            $( '.wikibrackets' ).show();

            // now we have to update the menu item 
            // (referred to in this script as "SRWikifySwitch"). 
            // To do that, first we remove it (if it exists):  
            if ( SRWikifySwitch ) {
                SRWikifySwitch.parentNode.removeChild(SRWikifySwitch);
            }

            // and then we create it (or its replacement) from scratch:
            SRWikifySwitch = mw.util.addPortletLink( 'p-tb', '', 'SR wikify \(turn off\)', 'tb-Wikify', 'Hide wiki brackets around links', '', '#t-specialpages' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brackets when clicked):
            $( SRWikifySwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                    SRWikifyOff();
            } );
        }
            
	    // ============ Function to sort the search results ==============
	    function SRSortOn() {
    	    // store status so it persists across page loads
	        localStorage.setItem("SRSortStatus", "on");

			// detach the the search resulsts, sort them, then put them back
			elems1 = $('.mw-search-results li').detach().sort(function (a, b) {
				return ($(a).text() < $(b).text() ? -1 
        			: $(a).text() > $(b).text() ? 1 : 0);
			}); 
			$('.mw-search-results').append(elems1);

	        // now we have to update the menu item 
    	    // (referred to in this script as "SRSortSwitch"). 
    	    // To do that, first we remove it (if it exists):  
    	    if ( SRSortSwitch ) {
    	        SRSortSwitch.parentNode.removeChild(SRSortSwitch);
    	    }

	        // and then we create it (or its replacement) from scratch:
	        SRSortSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR sort \(turn off\)', 'tb-Sort', 'Show search results in original order', '', '#tb-Wikify' );

	        // make the menu item clickable by binding it to a click handler
	        // (which activates the actions between the curly brackets when clicked):
	        $( SRSortSwitch ).click( function ( e ) {
	            e.preventDefault();     // prevents any default action -- we want only the following action to run: 
	                SRSortOff();
	        } );
	    }
   
	    // ============ Function to sort search results to original order ==============
	    function SRSortOff() {
	        // store status so it persists across page loads
	        localStorage.setItem("SRSortStatus", "off");

			// Development note: It would be best not to sort them to original order
			// if they are already in original order, such as when sort is off when 
			// the page is generalted.  A code inserted on the page itself could 
			// identify the page as alphabetically sorted. Therefore, this function 
			// should run only if that code (class = SRsorted) does not exist.

			// Sort search results by id, to return them to original order
			elems2 = $('.mw-search-results li').detach().sort(function (a, b) {
				return (parseInt(a.id) < parseInt(b.id) ? -1 
        			: parseInt(a.id) > parseInt(b.id) ? 1 : 0);
			}); 
			$('.mw-search-results').append(elems2);

	        // now we have to update the menu item 
	        // (referred to in this script as "SRSortSwitch"). 
	        // To do that, first we remove it (if it exists):  
	        if ( SRSortSwitch ) {
	            SRSortSwitch.parentNode.removeChild(SRSortSwitch);
	        }

	        // and then we create it (or its replacement) from scratch:
	        SRSortSwitch = mw.util.addPortletLink( 'p-tb', '', 'SR sort \(turn on\)', 'tb-Sort', 'Sort search results', '', '#tb-Wikify' );

	        $( SRSortSwitch ).click( function ( e ) {
	            e.preventDefault();     // prevents any default action -- we want only the following action to run:
	            SRSortOn();
	        } );
	    }
    } );
    } );
}( mediaWiki, jQuery ) );
// </syntaxhighlight>
Retrieved from "https://en.wikipedia.org/w/index.php?title=User:The_Transhumanist/SearchSuite.js&oldid=1199779749"