User:Cacycle/wikiWatch.js

Source: 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.
// <pre><nowiki>

// ==UserScript==
// @name        wikiWatch
// @namespace   http://en.wikipedia.org/wiki/User:Cacycle/
// @description A MediaWiki watchlist, recent changes, and user contributions page tool that sorts into namespaces, adds unwatch links, and auto-expands entries
// @include     /Special:
// @exclude
//
// @homepage    http://en.wikipedia.org/wiki/User:Cacycle/wikiWatch
// @source      http://en.wikipedia.org/wiki/User:Cacycle/wikiWatch.js
// @author      Cacycle (http://en.wikipedia.org/wiki/User:Cacycle)
// @license     Released into the public domain
// @version     0.9.5
// ==/UserScript==


//
// add wrapped wikiWatch code to body in order to access and use page scripts and variables under Greasemonkey
//

// start of global code wrapper
if (typeof(wwInstalledFlag) == 'undefined') { window.wwInstalledFlag = false; }

window.WWWrapper = function() {

	window.wwInstalledFlag = true;
	if (typeof(wwSetupFlag) == 'undefined') { window.wwSetupFlag = false; }
	var wwOpenPopup;
	var wwEditDivs = [];


//
// WWMain: wikiWatch setup
//

	window.WWMain = function() {

		WWRemoveEventListener(window, 'load', WWMain, false);

// check if this has already been run, either as a wiki gadget, wiki user script, or a Greasemonkey user script
		if (wwSetupFlag == true) {
			return;
		}
		wwSetupFlag = true;

// customizable definitions

// number of most recent entries in short display
		var wwRecentShort = wwRecentShort || 15;

// texts
		var WWText = WWText || {
			'WWRecentAll':       'All',
			'WWRecentAllToggle': '<span class="WWToggle" title="Toggle the display of all entries">(<a style="cursor: pointer;" onclick="javascript:{document.getElementById(\'WWSectionBlock-1-{day}\').style.display=\'block\';document.getElementById(\'WWSectionBlock-2-{day}\').style.display=\'none\';}">most recent only</a>)</span>',
			'WWRecentTop':       'All',
			'WWRecentTopToggle': '<span class="WWToggle" title="Toggle the display of all entries">(<a style="cursor: pointer;" onclick="document.getElementById(\'WWSectionBlock-2-{day}\').style.display=\'block\';document.getElementById(\'WWSectionBlock-1-{day}\').style.display=\'none\';">show all</a>)</span>',
			'WWUnwatch':         'unwatch'
		};

// css styles
		var WWCss = WWCss ||
			'.WWToggle         { font-weight: normal; }' +
			'.WWSectionFooter  { margin-left: 0.5em; }' +
			'.WWDayHeading     { background-color: #c0c0c0; border: gray solid 1px; margin: 1.25em 0 0.75em 0 !important; padding: 0 2em; }' +
			'.WWSectionHeading { margin: 0 0 0.5em 0; padding: 0 0 0.75em 0.5em; }' +
			'.WWSectionBlock   { margin: 0 0 0.75em 0; }' +
			'.WWSection        { border: gray solid 1px; padding: 0 0.5em 0.75em 0.5em; }' +
			'.WWSection > H5   { padding-top: 0.3em; padding-bottom: 0; }' +

			'.WWPopup          { display: block; padding: 0 1em 1.5em 2em; position: absolute; left: 17em; right: 1em; border: 1px solid black; border-top: none; border-right: 1px solid gray; }' +
			'.WWPopup TT       { display: none; }' +
			'.WWDebug          { display: none; position: static; margin-top: 3em; margin-left: 10em; zIndex: 1000000;}' +

//			'TD':                'text-indent: -2em; padding-left: 2em;',

			'.WWSection-1, .WWSection-1 DIV { background-color: #f8fcff; }' +
			'.WWSection-2, .WWSection-2 DIV { background-color: #f8fcff; }' +

			'.WWSection0, .WWSection0 DIV   { background-color: #ffffff; }' +
			'.WWSection1, .WWSection1 DIV   { background-color: #f2f2f2; }' +

			'.WWSection2, .WWSection2 DIV   { background-color: #e5e5e5; }' +
			'.WWSection3, .WWSection3 DIV   { background-color: #d0d0d0; }' +

// colors: HSB 40°-5%-100% (light), 40°-5%-95% (dark), 40° steps
			'.WWSection4, .WWSection4 DIV   { background-color: #fffbf2; }' +
			'.WWSection5, .WWSection5 DIV   { background-color: #f2eee6; }' +

			'.WWSection6, .WWSection6 DIV   { background-color: #fbfff2; }' +
			'.WWSection7, .WWSection7 DIV   { background-color: #eef2e6; }' +

			'.WWSection8, .WWSection8 DIV   { background-color: #f2fff2; }' +
			'.WWSection9, .WWSection9 DIV   { background-color: #e6f2e6; }' +

			'.WWSection10, .WWSection10 DIV { background-color: #f2fffb; }' +
			'.WWSection11, .WWSection11 DIV { background-color: #e6f2ee; }' +

			'.WWSection12, .WWSection12 DIV { background-color: #f2fbff; }' +
			'.WWSection13, .WWSection13 DIV { background-color: #e6eef2; }' +

			'.WWSection14, .WWSection14 DIV { background-color: #f2f2ff; }' +
			'.WWSection15, .WWSection15 DIV { background-color: #e6e6f2; }' +

			'.WWSection16, .WWSection16 DIV { background-color: #fbf2ff; }' +
			'.WWSection17, .WWSection17 DIV { background-color: #eee6f2; }' +

			'.WWSection18, .WWSection18 DIV { background-color: #fff2fb; }' +
			'.WWSection19, .WWSection19 DIV { background-color: #f2e6ee; }' +

			'.WWSection20, .WWSection20 DIV { background-color: #fff2f2; }' +
			'.WWSection21, .WWSection21 DIV { background-color: #f2e6e6; }' +

			'.WWSection22, .WWSection22 DIV { background-color: #fff2fb; }' +
			'.WWSection23, .WWSection23 DIV { background-color: #f2e6ee; }'
		;

// add stylesheet definitions
		var styles = new WWStyleSheet();
		styles.addRules(WWCss);

// detect pagetype and get the smallest container to replace for compatibility
		var wwWatchlist = false;
		var wwRecentchanges = false;
		var wwContributions = false;
		var container = document.body;
		if (mw.config.get('wgCanonicalSpecialPageName') == 'Watchlist') {
			wwWatchlist = true;
			var watchlistElement = document.getElementById('watchlist-message');
			if (watchlistElement != null) {
				container = watchlistElement.parentNode;
			}
		}
		else if (mw.config.get('wgCanonicalSpecialPageName') == 'Recentchanges') {
			wwRecentchanges = true;
			watchlistElement = document.getElementById('recentchangestext');
			if (watchlistElement != null) {
				container = watchlistElement.parentNode;
			}
		}
		else if (mw.config.get('wgCanonicalSpecialPageName') == 'Contributions') {
			wwContributions = true;
			watchlistElement = document.getElementById('contentSub');
			if (watchlistElement != null) {
				container = watchlistElement.parentNode;
			}
		}

		var regExpMatch;

// get watchlist block
		var sectionBlockIds = [];

// h4: watchlist, recent changes; ul, p: contributions
		var regExpWatchlist = new RegExp('(<select\\b[^>]*?\\bid="?namespace"?[^>]*>(.|\\s)*?)(<(h4|ul)\\b[^>]*>(.|\\s)*?)((<p>.*?</p>)?<div\\b[^>]*?\\bclass="?printfooter"?[^>]*>)', 'i');

// replace watchlist container
		container.innerHTML = container.innerHTML.replace(regExpWatchlist,
			function (p, p1, p2, p3, p4, p5, p6) {
				var namespaceBlock = p1;
				var watchlistBlock = p3;
				var pageBottom = p6;

// add unwatch links
				if ( (wwWatchlist == true) || (wwContributions == true) ) {

//                                                 12 link                         2 action3                  3     4     41
					watchlistBlock = watchlistBlock.replace(/((<a href=\"?[^\"]*?&amp;action=)history(\b[^\" ]*\"?[^>]*>)[^<]+(<\/a>))/gi, '$1, $2unwatch$3' + WWText['WWUnwatch'] + '$4');
				}

// get namespaces
				var namespaceName = [];
				var namespaceNumber = [];
				var namespaceHash = [];
				var ns = 0;
				var namespaceSelect = namespaceBlock;

// get namespace select
				if ( (regExpMatch = /(<select\b[^>]*?\bid=\"?namespace\"?[^>]*>(.|\s)*?<\/select>)/i.exec(namespaceSelect) ) != null) {
					namespaceSelect = regExpMatch[1];
				}

// cycle through namespace options
				var regExpNamespace = new RegExp('<option\\b[^>]*?\\bvalue="?(\\d+)"?[^>]*>((.|\\s)*?)</option>', 'gi');
				while ( (regExpMatch = regExpNamespace.exec(namespaceSelect) ) != null) {
					namespaceNumber[ns] = regExpMatch[1];
					namespaceName[ns] = regExpMatch[2];
					if (namespaceNumber[ns] == 0) {
						namespaceName[ns] = namespaceName[ns].substr(0, 1).toUpperCase() + namespaceName[ns].substr(1);
					}
					namespaceHash[ 'WW' + namespaceName[ns] ] = ns;
					ns ++;
				}
				var namespaceMax = ns;
				namespaceName[-1] = WWText['WWRecentTop'];
				namespaceName[-2] = WWText['WWRecentAll'];
				var namespaceSection = [];

// get day block
				var day = 0;
				var sorted = '';
				var regExpDays;

// contributions has no h4 but ul
				if (wwContributions == true) {
					regExpDays = new RegExp('((()))\\s*(<ul\\b[^>]*>)\\s*((.|\\s)*?)\\s*(</ul>)', 'gi');
				}

// watchlist and recent changes have h4 and optionally ul
				else {
					regExpDays = new RegExp('(<h4\\b[^>]*>\\s*((.|\\s)*?)\\s*</h4>)\\s*(<ul\\b[^>]*>)?\\s*((.|\\s)*?)\\s*(</ul>)?\\s*(?=(<h4\\b[^>]*>|$))', 'gi');
				}

// cycle through days or whole block
				while ( (regExpMatch = regExpDays.exec(watchlistBlock) ) != null) {
					var dayHeading = regExpMatch[1];
					var dayContainerTop = regExpMatch[4] || '';
					var dayContainer = regExpMatch[5];
					var dayContainerBottom = regExpMatch[7] || '';
					var mostRecent = 0;

// initialize and clear for +=
					for (var ns = -2; ns <= namespaceMax; ns ++) {
						namespaceSection[ns] = '';
					}
					namespaceSection[-2] = dayContainer;

// add day heading
					if (dayHeading != '') {
						sorted += '<div class="WWDayHeading">' + dayHeading + '</div>';
					}

// define regexps outside the loops
					var regExpArticles = new RegExp('((<(table|li)\\b[^>]*?>(.|\\s)*?</\\3>)\\s*(<div\\b[^>]*>(.|\\s)*?</div>)?)', 'gi');
					var regExpArticleName = new RegExp('<a\\b[^>]*?\\btitle=(\\"([^\\n\\>"]*)\\"|([^\\s\\">]*))[^>]*>(\\2|\\4)</a>', 'gi');

// get article block
					while ( (regExpMatch = regExpArticles.exec(dayContainer) ) != null) {
						var articleEditsBlock = regExpMatch[1];
						var articleBlock = regExpMatch[2];
						var editsBlock = regExpMatch[5];

// get article name
						while ( (regExpMatch = regExpArticleName.exec(articleBlock) ) != null) {
							var articleName = regExpMatch[4];

// get article namespace
							regExpMatch = /^(.*?):/.exec(articleName);
							var namespaceIndex = 0;
							if (regExpMatch != null) {
								namespaceIndex = namespaceHash[ 'WW' + regExpMatch[1] ];
							}

// append article block to section
							if (namespaceIndex != null) {
								namespaceSection[namespaceIndex] += articleEditsBlock;

// add to most recent section
								if (mostRecent < wwRecentShort) {
									namespaceSection[-1] += articleEditsBlock;
									mostRecent ++;
								}
							}
						}

// finish articles
					}

// finish sections
					for (var ns = -2; ns <= namespaceMax; ns ++) {

// sort the two main namespaces last
						if (ns == 0) {
							ns = 2;
						}
						else if (ns == namespaceMax) {
							ns = 0;
						}
						else if (ns == 2) {
							break;
						}

// skip empty sections
						if (namespaceSection[ns] == '') {
							continue;
						}

// rename all and top section elements
						if ( (ns == -2) || (ns == -1) ) {
							namespaceSection[ns] = namespaceSection[ns].replace(/(<[^>]*?\bid=(\"?)RC[IML]\d+)\2/g, '$1.' + ns + '$2');
							namespaceSection[ns] = namespaceSection[ns].replace(/(<[^>]*?=\"?javascript:toggleVisibility\(\'RC[IML]\d+)(\',\s*\'RC[IML]\d+)(\',\s*\'RC[IML]\d+)(\')/g, '$1.' + ns + '$2.' + ns + '$3.' + ns + '$4');
						}

						var sectionStyle = '';
						var headerToggle = '';
						var footerToggle = '';
						if (ns == -1) {
							var topToggle = WWText['WWRecentTopToggle'].replace(/\{day\}/g, day)
							headerToggle += ' ' + topToggle;
							footerToggle = '<div class="WWSectionFooter">' + topToggle + '</div>';
							if (wwRecentchanges == true) {
								sectionStyle = ' style="display: none;"';
							}
						}
						else if (ns == -2) {
							var allToggle = WWText['WWRecentAllToggle'].replace(/\{day\}/g, day)
							headerToggle = ' ' + allToggle;
							footerToggle = '<div class="WWSectionFooter">' + allToggle + '</div>';
							if (wwRecentchanges == false) {
								sectionStyle = ' style="display: none;"';
							}
						}

// add sections to sorted watchlist
						sorted += '<div class="WWSectionBlock"' + sectionStyle + ' id="WWSectionBlock' + ns + '-' + day + '"><div class="WWSection' + ns + '"><div class="WWSection"><h5 class="WWSectionHeading">' + namespaceName[ns] + headerToggle + '</h5>' + dayContainerTop + namespaceSection[ns] + dayContainerBottom + footerToggle + '</div></div></div>';

// remember section block names
						sectionBlockIds.push('WWSectionBlock' + ns + '-' + day);
					}

// finish day
					day ++;
				}

// return changed document.body
				return(namespaceBlock + '<div id="WWSorted" class="WWSorted">' + sorted + '</div>' + pageBottom);
			}
		);

// ceate list of expandable entries for closing popups
		var divs = document.getElementsByTagName('div');
		for (var i = 0; i < divs.length; i ++) {
			if (divs[i].id.match(/^RCI[\d\.\-]+/) != null) {
				wwEditDivs.push(divs[i]);
			}
		}

// regexp to identify article links from link href
		var regExpStr = '^' + mw.config.get('wgServer') + mw.config.get('wgArticlePath') + '$';
		regExpStr = regExpStr.replace(/\$\d/, '[^\\?]*?');
		regExpStr = regExpStr.replace(/(file:\/\/)([A-Z]:)(\/)/, '$1(/$2)?$3');
		regExpStr = regExpStr.replace(/\/()/g, '\\/');
		regExpStr = regExpStr.replace(/\./g, '\\.');
		var regExpArticleLink = new RegExp(regExpStr);

// get all page links
		var bodyContent = document.getElementById('bodyContent');
		if (bodyContent == null) {
			return;
		}
		var links = bodyContent.getElementsByTagName('a');

// cycle through all links
		var linkNumber = 0;
		for (var i = 0; i < links.length; i ++) {

// check if this is an article link
			if (links[i].innerHTML.replace(/\"/g, '') != links[i].title) {
				continue;
			}
			if (links[i].href.match(regExpArticleLink) == null) {
				continue;
			}

// add mouseover event to link
			WWAddEventListener(links[i], 'mouseover', WWOpenPopup, false);

// remove title
			links[i].title = '';

// find the container table element
			var mainTable = links[i];
				while (mainTable != null) {
				if (mainTable.nodeName == 'TABLE') {
					break;
				}
				mainTable = mainTable.parentNode;
			}

// check if next sibling is an edits div
			if (mainTable == null) {
				continue;
			}

			var editsDiv = mainTable.nextSibling;
			while (editsDiv != null) {
				if (editsDiv.nodeType != 3) {
					break;
				}
				editsDiv = editsDiv.nextSibling;
			}

// add link id for easy access of related edits div
			if (editsDiv == null) {
				continue;
			}
			if (editsDiv.nodeName != 'DIV') {
				continue;
			}
			var editsDivId = editsDiv.id;
			if (editsDivId.match(/^RCI[\d\.\-]+/) != null) {
				links[i].id = 'WW' + linkNumber + '-' + editsDivId;
				linkNumber ++;
			}
		}

// close popup when leaving a section
		for (var i = 0; i <	sectionBlockIds.length; i ++) {
			WWAddEventListener( document.getElementById( sectionBlockIds[i] ), 'mouseout', WWClosePopupsHandler, false );
		}

		return;
	}


//
// WWToggleVisibility: replacement for toggleVisibility
//

	function WWToggleVisibility(levelId, otherId, linkId) {

		var thisLevel = document.getElementById(levelId);
		var otherLevel = document.getElementById(otherId);
		var linkLevel = document.getElementById(linkId);

		if (thisLevel.className == 'WWPopup') {
			thisLevel.style.display = 'none';
			thisLevel.className = 'WWStandardExpand';
		}

		if (thisLevel.style.display == 'none')  {
			thisLevel.style.display = 'block';
			otherLevel.style.display = 'none';
			linkLevel.style.display = 'inline';
		}
		else {
			thisLevel.style.display = 'none';
			otherLevel.style.display = 'inline';
			linkLevel.style.display = 'none';
		}

		WWClosePopups(levelId);

		return;
	}
	var toggleVisibility = WWToggleVisibility;


//
// WWOpenPopup: open detailed edits preview
//

	function WWOpenPopup(event) {

// MS IE compatibility fix
		event = WWEvent(event);
		if (event == null) {
			return;
		}

// get link id with div RCI id
		var lnk = event.target
		var levelId;
		if (lnk == null) {
			return;
		}
		if (lnk.nodeName != 'A') {
			return;
		}

// only close popups for single-entry links
		if (lnk.id == '') {
			WWClosePopups();
		}

// popup edits
		else {
			levelId = lnk.id.replace(/^WW\d+-/, '');
			if (levelId != '') {
				var thisLevel = document.getElementById(levelId);

// show edits div as popup
				thisLevel.className = 'WWPopup';
				thisLevel.style.display = 'block';
				wwOpenPopup = thisLevel;

// down arrow back to right arrow
				var regExpMatch = /^RCI([\d\.\-]+)/.exec(levelId);
				if (regExpMatch != null) {

					var idNumber = regExpMatch[1];
					var otherLevel = document.getElementById('RCM' + idNumber);
					var linkLevel = document.getElementById('RCL' + idNumber);

					otherLevel.style.display = 'inline';
					otherLevel.blur();
					linkLevel.style.display = 'none';
				}
				WWClosePopups(levelId);
			}
		}
		return;
	}


//
// WWClosePopups: close all edit popups
//

	function WWClosePopups(levelId) {

		if (wwOpenPopup != null) {
			if (wwOpenPopup.id != levelId) {
				wwOpenPopup.className = '';
				wwOpenPopup.style.display = 'none';
				wwOpenPopup = null;
			}
		}
		for (var i = 0; i < wwEditDivs.length; i ++) {
			if (wwEditDivs[i].className == 'WWPopup') {
				if (wwEditDivs[i].id != levelId) {
					wwEditDivs[i].className = '';
					wwEditDivs[i].style.display = 'none';
				}
			}
		}
		return;
	}


//
// WWClosePopupsHandler: close all edit popups when leaving the section
//

	function WWClosePopupsHandler(event) {

// MS IE compatibility fix
		event = WWEvent(event);
		if (event == null) {
			return;
		}

		event.stopPropagation();
		if (event.relatedTarget != null) {
			if (event.relatedTarget.id != '') {
				if (event.relatedTarget.id.match(/\bRCI\d+\.+/) == null) {
					WWClosePopups();
				}
			}
		}
		return;
	}


//
// WWEvent: MS IE compatibility fix for event object
//

	function WWEvent(event) {

		var eventAlt;
		if (window.event != null) {
			eventAlt = window.event;
			if (eventAlt != null) {
				event = eventAlt;
				event.stopPropagation = function() {
					event.cancelBubble = true;
				};
				event.preventDefault = function() {
					event.returnValue = false;
				};
				event.target = event.srcElement;
				if (event.type == 'mouseout') {
			    event.relatedTarget = event.toElement;
				}
				else if (event.type == 'mouseover') {
			    event.relatedTarget = event.fromElement;
				}
			}
		}
		return(event);
	}


//
// WWAddEventListener: wrapper for addEventListener (http://ejohn.org/projects/flexible-javascript-events/)
//

	function WWAddEventListener(domElement, eventType, eventHandler, useCapture) {

		if (domElement != null) {
			if (domElement.attachEvent != null) {
				domElement['WW' + eventType + eventHandler] = eventHandler;
				domElement[eventType + eventHandler] = function() {
					domElement['WW' + eventType + eventHandler](window.event);
				}
				domElement.attachEvent('on' + eventType, domElement[eventType + eventHandler] );
			}
			else {
				domElement.addEventListener(eventType, eventHandler, useCapture);
			}
		}
		return;
	}


//
// WWRemoveEventListener: wrapper for removeEventListener
//

	function WWRemoveEventListener(domElement, eventType, eventHandler, useCapture) {

		if (domElement.detachEvent != null) {
			domElement.detachEvent('on' + eventType, domElement[eventType + eventHandler]);
			domElement[eventType + eventHandler] = null;
		}
		else {
			domElement.removeEventListener(eventType, eventHandler, useCapture);
		}
		return;
	}


//
// WWStyleSheet: create a new style sheet object
//

	function WWStyleSheet(contextObj) {

		if (contextObj == null) {
			contextObj = document;
		}
		this.styleElement = null;

// MS IE compatibility
		if (contextObj.createStyleSheet) {
			this.styleElement = contextObj.createStyleSheet();
		}

// standards compliant browsers
		else {
			this.styleElement = contextObj.createElement('style');
			this.styleElement.from = 'text/css';
			var insert = contextObj.getElementsByTagName('head')[0];
			if (insert != null) {
				this.styleElement.appendChild(contextObj.createTextNode(''));
				insert.appendChild(this.styleElement);
			}
		}


//
// WWStyleSheet.addRules: add all rules at once, much faster
//

		this.addRules = function(rules) {

// MS IE compatibility
			if (this.styleElement.innerHTML == null) {
				this.styleElement.cssText = rules;
			}

// via innerHTML
			else {
				this.styleElement.innerHTML = rules;
			}
			return;
		}
	}


//
// WWDebug: print the value of variables, use either a single value or a description followed by a value
//

	function WWDebug(objectName, object) {

// create debug textarea and add to debug wrapper
		if (window.WWDebugTextarea == null) {
			window.WWDebugTextarea = document.createElement('textarea');
			WWDebugTextarea.id = 'WWDebug';
			WWDebugTextarea.className = 'WWDebug';
			WWDebugTextarea.rows = 20;
			WWDebugTextarea = document.body.insertBefore(WWDebugTextarea, document.body.firstChild);
		}

		WWDebugTextarea.style.display = 'block';
		if (objectName == null) {
			WWDebugTextarea.value = '';
		}
		else {
			if (object == null) {
				WWDebugTextarea.value = objectName + '\n' + WWDebugTextarea.value;
			}
			else {
				WWDebugTextarea.value = objectName + ': ' + object + '\n' + WWDebugTextarea.value;
			}
		}
		return;
	}
	var WWD = WWDebug;

// schedule setup routine
	if ( (typeof(mw.config.get('wgCanonicalNamespace')) != 'undefined') && (typeof(mw.config.get('wgCanonicalSpecialPageName')) != 'undefined') ) {
		if (
			(mw.config.get('wgCanonicalNamespace') == 'Special') && (
				(mw.config.get('wgCanonicalSpecialPageName') == 'Watchlist') ||
				(mw.config.get('wgCanonicalSpecialPageName') == 'Recentchanges') ||
				(mw.config.get('wgCanonicalSpecialPageName') == 'Contributions')
			)
		) {
			WWAddEventListener(window, 'load', WWMain, false);
		}
	}

// close global wrapper
	return;
}

// append wrapper to head if run under Greasemonkey
if (window.parent == window) {
	if (typeof(GM_getValue) == 'function') {
		if ( (document.getElementById('WWHeadScript') == null) && (window.wwInstalledFlag == false) ) {
			window.wwInstalledFlag = true;
			var script = document.createElement('script');
			script.id = 'WWHeadScript';
			script.type = 'text/javascript';
			script.text = 'WWHeadWrapper = ' + WWWrapper.toString() + '; WWHeadWrapper();';
			document.getElementsByTagName('head')[0].appendChild(script);
		}
	}

// otherwise run installation directly
	else {
		WWWrapper();
	}
}

// </nowiki></pre>