Jump to content

User:The Transhumanist/ViewAnnotationToggler.js

From Wikipedia, the free encyclopedia
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">

/* anno.js: Adds a sidebar menu item and hot key to toggle annotations.

Based on: https://en.wikipedia.org/w/index.php?title=User:PleaseStand/hide-vector-sidebar.js&oldid=580854231
and https://en.wikipedia.org/wiki/User:Thespaceface/MetricFirst.js  

The hot key is Shift-Alt-a. 

IMPORTANT: The menu item for this script is not in the side bar menu. 
It is in the more tab at the top of the page. This is because having
it in the sidebar menu interferes with the script's page scrolling adjustment.

Currently, this script applies regex upon matches within the ID element 'mw-content-text'.
It wraps the annotations in <span class="anno"> and </span>. Then it hides/shows the elements
with that class. There is probably a much more efficient method than the one I use below.  If you happen to know of one, please let me know.

Besides that, the script isn't finished yet. The current problem I'm trying to solve is this:

Hiding or showing annotations affects the position of the viewport, so unfortunately,
the reader is jolted away from what he was reading.  This is bad.

I'd like the material that was in the viewport to stay there, which means the viewport
must be repositioned relative to the top of the page each time the toggle is activated.

If you have any ideas on how to fix this, I'd be most interested.

Sincerely,
The Transhumanist

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.

*/

// Start off with a bodyguard function to reserve mw and $ (see Explanatory notes on talk page).
( function ( mw, $ ) {

    // we can now rely on mw and $ within the safety of our “bodyguard” function, to mean 
    // "mediawiki" and "jQuery", respectively

    // ============== 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 "Editing " is in the page title (so it doesn't conflict with script editor)
		if (document.title.indexOf("Editing ") === 0) {
			// 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;
		}


        // =================== Prep work =====================
        var y1; var y2;
        var annoSwitch; 
        var cont = document.getElementById('mw-content-text');
        // wrap the annotations in spans with class "anno"
        cont.outerHTML = cont.outerHTML.replace(/(<li>.*?)( –.*)/g,'$1<span class="anno">$2</span>');

        // ================= Core control structure ================= 
        // Only activate on Vector skin
        if ( mw.config.get( 'skin' ) === 'vector' ) {
            $( function() {

                // get the value of our status variable from memory
                // (this tells us what mode to start in)
                var annostatus = localStorage.getItem('annostatus');

                // run the function corresponding to the current status
                if ( annostatus === "hide" ) {
                    annoHide();
                } else {
                    annoShow();
                }
            } );
        }

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

        // ============ Function to hide annotations ==============
        function annoHide() {
            // store status so it persists across page loads
            localStorage.setItem("annostatus", "hide");

            y1 = window.scrollY;
            // alert( "vertical scroll position is " + y1);

            //Select the set of annotations that are above where the viewpoint is scrolled to
            var $annos_above = $(".anno").filter( function(){
                var rect = this.getBoundingClientRect();
                if ( rect.bottom < 0 ) {
                    return true;
                } else {
                    return false;
                }
            } );

            //For each annotation above the viewport, get the difference in height in the containing element as that annotation is hidden
            var scroll_amount = 0;
            $annos_above.each( function(){
                var height_before = $(this).parent().outerHeight(true);
                $(this).hide();
                var height_after = $(this).parent().outerHeight(true);
                scroll_amount = scroll_amount + (height_after-height_before);
            } );

            //Hide the remaining annotations (hide elements with the anno class)
            $( ".anno" ).hide();

            window.scrollTo(0, y1);
            y1 = window.scrollY;
            // alert( "vertical scroll position is " + y1);

            //Scroll the window by the required amount
            window.scrollBy(0, scroll_amount);

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

            // and then we create it (or its replacement) from scratch:
            annoSwitch = mw.util.addPortletLink( 'p-cactions', '#', 'Annotations \(show\)', '', 'Show annotations', 'a' );
            // annoSwitch = mw.util.addPortletLink( 'p-tb', '#', 'Annotations \(show\)', 'ca-anno', 'Show the annotations', 'a' );

            // make the menu item clickable by binding it to a click handler
            // (which activates the actions between the curly brakets when clicked):
            $( annoSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run: 
                annoShow();
            } );
        }
   
        // ============ Function to show annotations ==============
        function annoShow() {
            // store status so it persists across page loads
            localStorage.setItem("annostatus", "show");

            // show the annotations (show elements with the anno class)
		$( ".anno").show();

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

            // and then we create it (or its replacement) from scratch:
            annoSwitch = mw.util.addPortletLink( 'p-cactions', '#', 'Annotations \(hide\)', '', 'Hide annotations', 'a' );
            // annoSwitch = mw.util.addPortletLink( 'p-tb', '#', 'Annotations \(hide\)', 'ca-anno', 'Hide the annotations', 'a' );

            $( annoSwitch ).click( function ( e ) {
                e.preventDefault();     // prevents any default action -- we want only the following action to run:
                annoHide();
            } );
        }
    } );
}( mediaWiki, jQuery ) );
// </syntaxhighlight>

// sample code for getting and setting viewport position:
// y = window.scrollY;
// alert( "vertical scroll position is " + y);
// window.scrollTo(0, y);