// <pre><nowiki>
// ==UserScript==
// @name wikiWatch
// @namespace http://en.wikipedia.orghttps://wiki386.com/en/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.orghttps://wiki386.com/en/User:Cacycle/wikiWatch
// @source http://en.wikipedia.orghttps://wiki386.com/en/User:Cacycle/wikiWatch.js
// @author Cacycle (http://en.wikipedia.orghttps://wiki386.com/en/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=\"?*?&action=)history(\b*\"?*>)+(<\/a>))/gi, '$1, $2unwatch$3' + WWText + '$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;
}
// cycle through namespace options
var regExpNamespace = new RegExp('<option\\b*?\\bvalue="?(\\d+)"?*>((.|\\s)*?)</option>', 'gi');
while ( (regExpMatch = regExpNamespace.exec(namespaceSelect) ) != null) {
namespaceNumber = regExpMatch;
namespaceName = regExpMatch;
if (namespaceNumber == 0) {
namespaceName = namespaceName.substr(0, 1).toUpperCase() + namespaceName.substr(1);
}
namespaceHash ] = ns;
ns ++;
}
var namespaceMax = ns;
namespaceName = WWText;
namespaceName = WWText;
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;
var dayContainerTop = regExpMatch || '';
var dayContainer = regExpMatch;
var dayContainerBottom = regExpMatch || '';
var mostRecent = 0;
// initialize and clear for +=
for (var ns = -2; ns <= namespaceMax; ns ++) {
namespaceSection = '';
}
namespaceSection = 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=(\\"(*)\\"|(*))*>(\\2|\\4)</a>', 'gi');
// get article block
while ( (regExpMatch = regExpArticles.exec(dayContainer) ) != null) {
var articleEditsBlock = regExpMatch;
var articleBlock = regExpMatch;
var editsBlock = regExpMatch;
// get article name
while ( (regExpMatch = regExpArticleName.exec(articleBlock) ) != null) {
var articleName = regExpMatch;
// get article namespace
regExpMatch = /^(.*?):/.exec(articleName);
var namespaceIndex = 0;
if (regExpMatch != null) {
namespaceIndex = namespaceHash ];
}
// append article block to section
if (namespaceIndex != null) {
namespaceSection += articleEditsBlock;
// add to most recent section
if (mostRecent < wwRecentShort) {
namespaceSection += 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 == '') {
continue;
}
// rename all and top section elements
if ( (ns == -2) || (ns == -1) ) {
namespaceSection = namespaceSection.replace(/(<*?\bid=(\"?)RC\d+)\2/g, '$1.' + ns + '$2');
namespaceSection = namespaceSection.replace(/(<*?=\"?javascript:toggleVisibility\(\'RC\d+)(\',\s*\'RC\d+)(\',\s*\'RC\d+)(\')/g, '$1.' + ns + '$2.' + ns + '$3.' + ns + '$4');
}
var sectionStyle = '';
var headerToggle = '';
var footerToggle = '';
if (ns == -1) {
var topToggle = WWText.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.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 + headerToggle + '</h5>' + dayContainerTop + namespaceSection + 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.id.match(/^RCI+/) != null) {
wwEditDivs.push(divs);
}
}
// 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:\/\/)(:)(\/)/, '$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.innerHTML.replace(/\"/g, '') != links.title) {
continue;
}
if (links.href.match(regExpArticleLink) == null) {
continue;
}
// add mouseover event to link
WWAddEventListener(links, 'mouseover', WWOpenPopup, false);
// remove title
links.title = '';
// find the container table element
var mainTable = links;
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+/) != null) {
links.id = 'WW' + linkNumber + '-' + editsDivId;
linkNumber ++;
}
}
// close popup when leaving a section
for (var i = 0; i < sectionBlockIds.length; i ++) {
WWAddEventListener( document.getElementById( sectionBlockIds ), '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(+)/.exec(levelId);
if (regExpMatch != null) {
var idNumber = regExpMatch;
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.className == 'WWPopup') {
if (wwEditDivs.id != levelId) {
wwEditDivs.className = '';
wwEditDivs.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 = eventHandler;
domElement = function() {
domElement(window.event);
}
domElement.attachEvent('on' + eventType, domElement );
}
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);
domElement = 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');
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').appendChild(script);
}
}
// otherwise run installation directly
else {
WWWrapper();
}
}
// </nowiki></pre>