User:The Transhumanist/ViewAnnotationToggler.js: Difference between revisions

Content deleted Content added
remove HTML id (optional)
update comment
 
(310 intermediate revisions by the same user not shown)
Line 1:
// <syntaxhighlight lang="javascript">
/*
anno.js
Annotation toggle
Hides annotations in list items
Applies to annotations everywhere,
whether in outlines, list pages, or embedded lists.
Attributions/credits:
DOM element placement subroutine based on: https://en.wikipedia.org/w/index.php?title=User:TheDJ/thetranshumanist.js&oldid=746366603
Toggle switch subroutine based on: https://en.wikipedia.org/w/index.php?title=User:PleaseStand/hide-vector-sidebar.js&oldid=580854231
*/
 
/* anno.js: Adds a sidebar menu item and hot key to toggle annotations.
// The DOM element placement subroutine
if ( mw.config.get('wgAction') === 'view' ) {
mw.hook( 'wikipage.content').add( function ($contents ) {
var listItems = $contents.find( 'li:not([class]):not([id])' );
listItems.each( function( index, li ) {
var nodes = $(li).contents();
var marker = false;
var ul = false;
for ( var i = 0; i < nodes.length; i++ ) {
if ( nodes[i].nodeType === Node.TEXT_NODE &&
nodes[i].textContent.indexOf(' – ') >= 0)
{
marker = i;
break;
}
}
if( nodes[nodes.length-1].tagName === "UL" ) {
ul = nodes[nodes.length-1];
}
if( marker > 0 ) {
var wrapper = $('<span>').addClass('myscript-wrapper');
wrapper.append(nodes.slice(marker, ul ? nodes.length-1 : nodes.length));
$(li).append(wrapper);
if ( ul ) {
$(li).append( ul );
}
}
});
// The toggle switch subroutine
( function ( mw, $ ) {
var annoSwitch;
function annoShow() {
document.getElementsByClassName('myscript-wrapper').style.visibility = '';
if ( annoSwitch ) {
annoSwitch.parentNode.removeChild(annoSwitch);
}
annoSwitch = mw.util.addPortletLink( 'p-cactions', '#', 'Hide annotation', '', 'Hide list item descriptions', 'a' );
$( annoSwitch ).click( function ( e ) {
e.preventDefault();
annoHide();
} );
}
 
Based on: https://en.wikipedia.org/w/index.php?title=User:PleaseStand/hide-vector-sidebar.js&oldid=580854231
function annoHide() {
and https://en.wikipedia.org/wiki/User:Thespaceface/MetricFirst.js
document.getElementsByClassName('myscript-wrapper').style.visibility = 'hidden';
if ( annoSwitch ) {
annoSwitch.parentNode.removeChild(annoSwitch);
}
annoSwitch = mw.util.addPortletLink( 'p-cactions', '#', 'Hide annotation', '', 'Hide list item descriptions', 'a' );
$( annoSwitch ).click( function ( e ) {
e.preventDefault();
annoShow();
} );
}
// Only activate on Vector skin
if ( mw.config.get( 'skin' ) === 'vector' ) {
$( function() {
// Change this if you want to hide annotations by default
annoShow();
} );
}
}( mediaWiki, jQuery ) );
});
}
 
The hot key is Shift-Alt-a.
// //////////////////////////////////////////////////////////////////////////
 
IMPORTANT: The menu item for this script is not in the side bar menu.
/* Walk through of above code (with extensive remarks)
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'.
// Run only on view (not edit) pages. See [[mw:Manual:Interface/JavaScript#Page-specific]]
It wraps the annotations in <span class="anno"> and </span>. Then it hides/shows the elements
if ( mw.config.get('wgAction') === 'view' ) {
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:
// Attach a function to be run once the 'wikipage.content' part of a page is available
 
mw.hook( 'wikipage.content').add( function ($contents ) {
Hiding or showing annotations affects the position of the viewport, so unfortunately,
// Like document.ready, this will run, once that part of the page is
the reader is jolted away from what he was reading. This is bad.
// ready/updated.
 
// $contents is a jQuery list of DOM elements, that has just become available.
I'd like the material that was in the viewport to stay there, which means the viewport
// See also: http://api.jquery.com/jquery/
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.
 
// Get all the <li> elements from $contents, but skip those with a class or ID,
// because they might have special functionality that you don't want to break.
// We generally avoid things like this, because they will break easily.
// Wikipedia is so diverse and big, that to do anything,
// your content really needs a class or ID.
var listItems = $contents.find( 'li:not([class]):not([id])' );
// Iterate over each of the <li> items
listItems.each( function( index, li ) {
// This part is complicated, because we need to look at text
// and text is not structured. Get the li item, and wrap it with jquery
// Then get all the direct children nodes
var nodes = $(li).contents();
var marker = false;
var ul = false;
for ( var i = 0; i < nodes.length; i++ ) {
// We need to find text nodes, that have our - marker.
if ( nodes[i].nodeType === Node.TEXT_NODE &&
nodes[i].textContent.indexOf(' – ') >= 0)
{
// We found the node which contains our marker
marker = i;
break;
}
}
// Check to see if the last node is an UL, so we don't break nesting
if( nodes[nodes.length-1].tagName === "UL" ) {
ul = nodes[nodes.length-1];
}
// only useful if it's the second element or later
if( marker > 0 ) {
// Use jquery to create a new span
// We use span, because it is an inline element.
// We give it a class so we can find it back later
// This element is already part of the document, but it is not attached to anything visible yet.
var wrapper = $('<span>').addClass('myscript-wrapper');
// Move the node with our marker, and all nodes that follow it into this new wrapper.
// This removes them from the original <li>
wrapper.append(nodes.slice(marker, ul ? nodes.length-1 : nodes.length));
// Append the wrapper to our original list item to make the wrapper visible.
$(li).append(wrapper);
if ( ul ) {
$(li).append( ul );
}
}
});
// Now we have structure, and we can apply our manipulations and functionality
// We simply hide all our elements
$( '.myscript-wrapper' ).hide();
// You would now add controls to show them etc. etc.
});
}
*/
 
// Start off with a bodyguard function to reserve mw and $ (see Explanatory notes on talk page).
/* Further notes (tutorial)
( function ( mw, $ ) {
 
// we can now rely on mw and $ within the safety of our “bodyguard” function, to mean
var i = 0;
// "mediawiki" and "jQuery", respectively
i++;
// This runs immediately.
// It is safe to do things like this, UNLESS they need to change something in the page.
// You CAN use it to do data processing, start requests to the api etc.
 
// ============== ready() event listener/handler ==============
// An anonymous function block wrapped inside $()
// below is jQuery short-hand for $(document).ready(function() { ... });
$( function() {
// it makes the rest of the script wait until the page's DOM is loaded and ready
// do stuff to change the page
$(function() {
});
// $() is an alias for jQuery(), the main function of the jQuery library,
// ============== deactivation filters (guard clauses) ==============
// which is always available for script writers on Wikipedia
// End the script if "Editing " is in the page title (so it doesn't conflict with script editor)
// When used like this, it is a shortcut for $( document ).ready(), and it will
if (document.title.indexOf("Editing ") === 0) {
// execute your anonymous function as soon as the page is ready to be
// use a return statement to end the local function and hence the program's body
// manipulated by JS, or immediately if it is ready now.
// important: this approach does not work outside of a function
// See also: http://api.jquery.com/jquery/#jQuery3
return;
}
 
// Attach a function to be run once the 'wikipage.content' part of a page is available
mw.hook( 'wikipage.content').add( function ($content ) {
// Like document.ready, this will run once that part of the page is
// ready/updated.
// $content is a jQuery list of DOM elements, that has just become available.
// See also: http://api.jquery.com/jquery/
});
// This is used by many scripts in MediaWiki, because it also works after
// saving with Visual Editor, or when previewing with Live preview tools etc.
// There are several such named hooks like:
// wikipage.diff, wikipage.editform, wikipage.categories
 
// =================== Prep work =====================
// mw, an alias for mediaWiki, is another javascript libary that is always
var y1; var y2;
// available to MediaWiki and Wikipedia scripters.
var annoSwitch;
// See also: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw
var cont = document.getElementById('mw-content-text');
// For the hook specific part of this library, which we use above, see:
// wrap the annotations in spans with class "anno"
// https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook
cont.outerHTML = cont.outerHTML.replace(/(<li>.*?)( –.*)/g,'$1<span class="anno">$2</span>');
 
// ================= Core control structure =================
// Note that large parts of this mw library are not ready for usage by default,
// Only activate on Vector skin
// and you might have to load some parts using mw.loader, before they can be used.
if ( mw.config.get( 'skin' ) === 'vector' ) {
// For loading dependencies and guaranteeing order between dependencies,
$( function() {
// use ResourceLoader.
// See also: https://www.mediawiki.org/wiki/ResourceLoader/Developing_with_ResourceLoader#Client-side_.28dynamically.29
*/
 
// 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" ) {
//List item processor
annoHide();
//Based on a script by Writ Keeper
} else {
//This selector grabs all of the <li> elements that are within #mw-content-text, and then does something for each of them
annoShow();
$("#mw-content-text li").each( function (ind, el)
}
{
} );
//first, grab the text from the current li element; we want only the top level text, so filter out all of the other stuff.
}
var currentText = $(el).contents().filter(function(){return this.nodeType === 3; });
 
// ======================== Subroutines ===========================
//Process only if there's something to process
// Functions (aka subroutines) are activated only when they are called.
if(currentText.text().length > 0)
// 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
//replace the character
// is easier to follow.
var newText = currentText.text().replace(/–/g,'XXX');
 
// ============ Function to hide annotations ==============
//reinsert the updated string back into the DOM
function annoHide() {
currentText.replaceWith(newText);
// 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);