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

Content deleted Content added
add test script by TheDJ
update comment
 
(317 intermediate revisions by the same user not shown)
Line 1:
// <syntaxhighlight lang="javascript">
/*
Test script by TheDJ
For processing list items:
to replace each en dash with XXX
*/
 
/* anno.js: Adds a sidebar menu item and hot key to toggle annotations.
var i = 0;
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.
 
Based on: https://en.wikipedia.org/w/index.php?title=User:PleaseStand/hide-vector-sidebar.js&oldid=580854231
// A anonymous function block wrapped inside $()
and https://en.wikipedia.org/wiki/User:Thespaceface/MetricFirst.js
$( function() {
// do stuff to change the page
});
// $() is an alias for jQuery(), the main function of the jQuery library,
// which is always available for script writers on Wikipedia
// When used like this, it is a shortcut for $( document ).ready(), and it will
// execute your anonymous function as soon as the page is ready to be
// manipulated by JS, or immediately if it is ready now.
// See also: http://api.jquery.com/jquery/#jQuery3
 
The hot key is Shift-Alt-a.
// Attach a function to be run once the 'wikipage.content' part of a page is available
mw.hook( 'wikpage.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
 
IMPORTANT: The menu item for this script is not in the side bar menu.
// mw, an alias for mediaWiki, is another javascript libary that is always
It is in the more tab at the top of the page. This is because having
// to MediaWiki and Wikipedia scripters.
it in the sidebar menu interferes with the script's page scrolling adjustment.
// See also: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw
// For the hook specific part of this library, which we use above, see:
// https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.hook
 
Currently, this script applies regex upon matches within the ID element 'mw-content-text'.
// Note that large parts of this mw library are not ready for usage by default,
It wraps the annotations in <span class="anno"> and </span>. Then it hides/shows the elements
// and you might have to load some parts using mw.loader, before they can be used.
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.
// For loading dependencies and guaranteeing order between dependencies,
// use ResourceLoader.
// See also: https://www.mediawiki.org/wiki/ResourceLoader/Developing_with_ResourceLoader#Client-side_.28dynamically.29
 
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.
// Example
if ( mw.config.get('wgAction') === 'view' ) {
mw.hook( 'wikipage.content').add( function ($contents) {
// 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.
});
}
 
Sincerely,
/*
The Transhumanist
//List item processor
 
//Based on a script by Writ Keeper
Brief comments are provided within the sourcecode below. For extensive explanatory
//This selector grabs all of the <li> elements that are within #mw-content-text, and then does something for each of them
notes on what the source code does and how it works, see the Script's workshop on
$("#mw-content-text li").each( function (ind, el)
the talk page.
{
//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; });
 
//Process only if there's something to process
if(currentText.text().length > 0)
{
//replace the character
var newText = currentText.text().replace(/–/g,'XXX');
//reinsert the updated string back into the DOM
currentText.replaceWith(newText);
}
});
*/
 
// 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);