User:Cacycle/wikiWatch.js

//

// ==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': ' (most recent only) ', 'WWRecentTop':      'All', 'WWRecentTopToggle': ' (show all) ', '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('(]*?\\bid="?namespace"?[^>]*>(.|\\s)*?)(<(h4|ul)\\b[^>]*>(.|\\s)*?)(( .*? )?]*?\\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>))/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 = /(]*?\bid=\"?namespace\"?[^>]*>(.|\s)*?<\/select>)/i.exec(namespaceSelect) ) != null) { namespaceSelect = regExpMatch[1]; }

// cycle through namespace options var regExpNamespace = new RegExp(']*?\\bvalue="?(\\d+)"?[^>]*>((.|\\s)*?) ', '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*(]*>)\\s*((.|\\s)*?)\\s*()', 'gi'); }

// watchlist and recent changes have h4 and optionally ul				else { regExpDays = new RegExp('(]*>\\s*((.|\\s)*?)\\s* )\\s*(]*>)?\\s*((.|\\s)*?)\\s*()?\\s*(?=(]*>|$))', '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 += ' ' + dayHeading + ' '; }

// define regexps outside the loops var regExpArticles = new RegExp('((<(table|li)\\b[^>]*?>(.|\\s)*?)\\s*(<div\\b[^>]*>(.|\\s)*? )?)', '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 = ' ' + topToggle + ' '; if (wwRecentchanges == true) { sectionStyle = ' style="display: none;"'; }						}						else if (ns == -2) { var allToggle = WWText['WWRecentAllToggle'].replace(/\{day\}/g, day) headerToggle = ' ' + allToggle; footerToggle = ' ' + allToggle + ' '; 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 + '"> <h5 class="WWSectionHeading">' + namespaceName[ns] + headerToggle + ' ' + dayContainerTop + namespaceSection[ns] + dayContainerBottom + footerToggle + '  ';

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

// finish day day ++; }

// return changed document.body return(namespaceBlock + ' ' + sorted + ' ' + 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; } }

//