Wikipedia:User scripts/Techniques
This page will collect various techniques for achieving common tasks needed in writing user scripts. Discussion about limitations, relative portability, and speed of the various alternatives is strongly encouraged. There is a lot of duplication and non-optimal efforts out there, and this will hopefully encourage us to write tighter, more correct code, both easier and faster.
An advanced API for interacting with Wikipedia content is being developed, large parts of which are already operational. The various possibilities are described at mw:API. The idea is to send an AJAX request (see below) to the API containing a query. The result of this query can be returned in several formats, of which JSON is perhaps the most useful, see below.
Identifying the type of page
This refers to techniques for identifying the current namespace of the current page, whether or not it is an edit page, a preview page, a Special page, etc.
Preview pages
document.getElementById("wikiPreview")
- From: Add Edit Top Link
Edit pages
document.getElementById('ca-edit').className == 'selected'
- Based on: Add edit section 0
- monobook-only
document.title.indexOf("Editing ") == 0
or
wgAction=='edit'
- From: Quick wikify
/[?&]action=edit/.test(window.location.href)
- Based on: Force edit summary
- This may fail when Wikipedia is under heavy load and you hit Save page but get a preview instead. Then the url contains action=submit.
document.editform
- Should work regardless of skin and language
History pages
window.location.href.indexOf("&action=history") != -1
or
wgAction=='history'
- Based on: Changes since I last edited
Special pages
window.location.href.indexOf("Special:") != -1
- From: Add Edit Top Link
!/wiki\/Special:|w\/index.php?title=Special:/.test(window.location.href)
wgCanonicalNamespace == "Special"
Pages with history
document.getElementById('ca-history')
- From: Add purge to tabs
Editable pages
document.getElementById('ca-edit')
- From: Add edit section 0
Getting various parts of a page
Getting the page title
See Wikipedia:WikiProject User scripts/Scripts/Get Page Name and Wikipedia:WikiProject User scripts/Scripts/Get tidy title.
var pagetitleRe=/[^:]*:\/\/en\.wikipedia\.org\/(wiki\/|w\/index\.php\?title=)([^&?]*)/; pagetitleRe.exec(decodeURI(location.href))[2].split('_').join(' ');
- This tries to get the article title from the URL
document.getElementsByTagName('h1')[0].firstChild.nodeValue
- This grabs the title at the top of the page (for example, "Editing Wikipedia:WikiProject User scripts/Techniques (section)").
Getting the various toolbars (personal, tabs, sidebar)
var tabs = document.getElementById(BAR NAME).getElementsByTagName('ul')[0];
- Where BAR NAME is one of the following strings:
- 'p-cactions'
- the tabs at the top of the page (with the article, discussion, edit, history, move, and watch links)
- 'p-personal'
- the personal toolbar (i.e. the one at the top, with a link to user page, user talk, prefs, watchlist, contribs, log out)
- 'p-navigation'
- the navigation toolbar (i.e. Main page, Featured Content, etc.)
- 'p-interaction'
- the interaction toolbar, below the navigation toolbar
- 'p-tb'
- the toolbox (What links here, Related changes etc.)
- This gets the toolbar in a form that can be the first arg to Wikipedia:User scripts/Scripts/Add LI link
TODO: Someone please test the search and toolbox ones, and see if they work the same. Thanks!
- The search box is 'p-search' but there's no <ul> element in it. [ælfəks] 10:38, 24 June 2006 (UTC)
Inserting content
document.getElementById("content").insertBefore(document.createTextNode("abcdef"), document.getElementsByTagName("h1")[0])
- On a page with a h1 heading, this works in Firefox 1.0.4 on OSX, but fails on some other browsers. Anyone know how or why? JesseW 20:58, 29 August 2005 (UTC)
- No, but does it help if you delay execution until the page has loaded? Lupin|talk|popups 12:14, 4 October 2005 (UTC)
Pressing buttons
document.editform.wpDiff.click()
- Presses the diff button.
Accesskeys and link titles
Things to add, modify and remove accesskeys and link titles for list items with an id tag.
Add accesskey and title
ta['ca-purge'] = ['g', 'Purge the internal cache for this page'];
- From: Add purge to tabs
Disabling accesskeys and titles
ta['pt-logout'] = null;
Update accesskeys and titles
akeytt();
- From: Add purge to tabs
Altering existing interface links
To change the url, name, or any other aspect of existing tab buttons, personal bar links, or other links, use the following: (where id is the id of the link to be changed, e.g. "pt-preferences", "ca-edit", "n-portal" or "t-whatlinkshere"; url is the new URL, and name is the new displayed name for the link, e.g. "my preferences", "edit this page", "Community Portal", or "What links here")
document.getElementById(id).childNodes[0].href=url q=document.getElementById(id).firstChild; q.removeChild(q.firstChild); q.appendChild(document.createTextNode(name))
Onload Structure
wikibits.js contains a function called addOnloadHook() that attaches functions to the onLoad event:
addOnloadHook( myFunction );
Functions can also be written inline as
addOnloadHook( function() { // Code here } );
Do not assign window.onload to a function directly, as this overwrites any other onLoad functions that may have been previously set.
Note: This technique can cause an error message in some situations. If you need the whole page to be loaded when your hook executes, try:
hookEvent("load", myHookFunction);
Include an external js-file on wikipedia
function import_external(page) { document.write('<script type="text/javascript" src="' + 'http://en.wikipedia.org/w/index.php?title=' + page + '&action=raw&ctype=text/javascript"></script>'); } import_external('User:Ilmari Karonen/godmode-light.js');
AJAX
If you want to read the content from a webpage from JavaScript, and have AJAX fun, use the predefined function sajax_init_object
:
javascript:void(a=sajax_init_object(),a.open("GET","http://enter.the.name.of.a.website.here",true),a.onreadystatechange=function(){if(a.readyState!=4)return;alert("["+a.status+":"+a.statusText+"]\n"+a.responseText);},a.send())
You can copy the above code to the address bar of your browser to test it.
The same in a more friendly layout, like you would use in a script file:
a=sajax_init_object();
a.open("GET", "http://enter.the.name.of.a.website.here", true);
a.onreadystatechange = function()
{
if(a.readyState != 4) return;
alert("[" + a.status + ":" + a.statusText + "]\n" + a.responseText);
};
a.send();
Retrieving the wikitext of a page
It's easy! Test it:
javascript:void(a=sajax_init_object(),a.open("GET",wgServer+wgScriptPath+"/index.php?title=Knights_of_the_Lambda_Calculus&action=raw",true),a.onreadystatechange=function(){if(a.readyState!=4)return;alert("["+a.status+":"+a.statusText+"]\n"+a.responseText);},a.send(null))
And again, in a more friendly layout:
a=sajax_init_object(); a.open("GET", wgServer + wgScriptPath + "/index.php?title=Knights_of_the_Lambda_Calculus&action=raw", true); a.onreadystatechange = function() { if(a.readyState != 4) return; alert("[" + a.status + ":" + a.statusText + "]\n" + a.responseText); }; a.send(null);
Editing the wikitext of a page
- Currently work is underway to make this solution unnecessary in most situations. See mw:API.
One might be tempted to use the same AJAX method as above, first GET'ing the edit page, then POST'ing the new data. Unfortunately this won't work. IE refuses to load the HTML of the edit page as XML, because its parser says the HTML is not valid XML or something. I tried POST'ing without first asking for an edit form, but that doesn't work either. A few solutions would be:
- Write an HTML parser in JavaScript. Good luck.
- Try to locate wpTextbox1 using regexen. Fragile.
- Look for the body-tag and insert the contents of that tag in a DIV using innerHTML.
- It's easier to use the next, similar solution.
- Don't use AJAX, but an IFRAME instead.
The last solution mimics the user navigating to the edit page, modifying, and saving. You can try it out, if you wish:
javascript:void(f=document.createElement("IFRAME"),f.src=wgServer+wgScriptPath+"/index.php?title=Wikipedia:Sandbox&action=edit",document.body.appendChild(f))
Note that the IFRAME appears at the bottom left of the document. Wait until it has finished loading. IFRAME's trigger an event when that happens, so this can be done programmatically as well.
javascript:void(e=f.contentWindow.document.editform,e.wpTextbox1.value+="\n\nTest message.",e.wpSummary.value="faux AJAX test",e.wpSave.click())
You have now posted "Test message." to the sandbox.
Pros
- Since it mimics the user doing normal modifications, it could probably be made to work in every browser.
- You have access to all normal editing tools, including diff functionality, and any specialized scripts that you have configured to only load on edit pages.
- It's comparatively easy to use.
- No risk of breaking UTF-8 characters, something which can happen when using uncareful AJAX scripts.
Cons
- The server has to prepare an HTML form for you, and the browser has to parse it. Since the edit is made automatically, this may mean wasted server and client time.
- User scripts will load, dragging down performance. (Although this might also be a good thing, see above.)
- You may find it desirable to hide the IFRAME.
- This solution just feels cludgy.
Automatic edits
On edit pages you can find the textbox with the wikitext like this:
var t = document.editform.wpTextbox1;
Then you can read and write the wikitext:
if(/^\s*$/.test(t.value)) ... ; t.value = t.value + "\n==New section==\n";
JSON
Parsing JSON text, as delivered by e.g. the MediaWiki API. The API always returns resultText's of the form "{ ... }"; they can be evaluated (parsed) in several ways:
- Direct assignment
eval("myvar=" + r)
- Using braces
eval("(" + r + ")")
- Using null
eval("null," + r)
- Using undefined
eval("undefined," + r)
What is faster/more efficient? Someone needs to run a few benchmarks.
Update a script
Scripts on a user's computer are updated to the most recent version version by bypassing (clearing) the browser cache - the user has to push Shift-Reload (Mozilla) or Shift-F5 (MS-IE). A JavaScript can do the same by calling:
window.location.reload(true);
This forced reload ("forceGet") immediately reloads the current page including all images, scripts, and stylesheets. This should not be done from edit or preview pages as the edits might get lost.