User:Js/watchlist.js
Appearance
< User:Js
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:Js/watchlist. |
var WLScript = new function(){ //wrapper object
var whenPageLoaded = +(new Date()) - 20000 //add 20 sec just in case
var namespace, $content, alreadySorted, alreadyAddedUnwatch, mm
var inProgress = null, timeoutID = null, isEnhanced
mm = {
sort:'↑↓',
sortTip:'Sort rows by even namespace number, then by page title',
sortDone:'Watchlist changes already sorted',
unwatchTip:'Add (x) quick unwatch links',
unwatchDone:'Use (x) links to quickly unwatch pages',
onlynew: 'Only new',
onlynewTip: 'Show changes since this page was loaded',
expandAll: 'Expand/hide all',
fullPage: 'Hide/Show interface'
}
this.onLoad = function() {
namespace = document.getElementById('namespace')
if (!namespace) return
$content = $('#bodyContent')
if (!$content.length){
$content = $('#mw_content, #article').eq(0)
if (!$content.length) $content = $(document.body) // covered all the skin but who knows...
}
//enhanced RC: make (x) appear by clicking on timestamp
isEnhanced = $content.find('ul.special').length == 0
if (isEnhanced) $content.click(toggleXLink)
//insertion point: after "days all"
var insAt = $('#mw-watchlist-options').find('a[href*=&days=7]').next().next()
if (!insAt.length) insAt = $('#mw-watchlist-options').find('hr').last()
//"only new" link
var lnk = addLnk(onlyNewEntries, mm.onlynew, mm.onlynewTip)
.attr('id', 'listSince').mousedown(onlyNewEntries) //middle clicks too
//"maximized mode" link
$('#firstHeading').append(
lnk.clone(true).addClass('wl-maximized'),
$('<span class=wl-maximized id=wl-restore>↙</span>').click(maximize)
)
appendCSS('.wl-maximized { margin-left:1em; display:none}\
#wl-restore {margin-left:2em; cursor:pointer; font-size:1.6em;\
border:1px outset gray; border-bottom:none; padding:0 2px}')
//unwatch link(s)
if (window.unwatchLinksOnLoad) addUnwatchLinks() //enwiki
else addLnk(addUnwatchLinks, 'x' , mm.unwatchTip)
//other links
addLnk(sortWatchlist, mm.sort, mm.sortTip)
if ($('#mw-rc-openarrow-0').length) addLnk(expandWatchlist, '±', mm.expandAll)
addLnk(maximize, '↗', mm.fullPage)
return
function addLnk(fnc, txt, tip){
var a = $j('<a href=# title="'+tip+'" style="font-style:italic">'+txt+'</a>').click(fnc)
insAt.before(' | ', a)
return a
}
}
function onlyNewEntries(e) {
var url = window.location.href.split('#')[0]
var days = ( +(new Date()) - whenPageLoaded)/(1000 * 3600 * 24)
if (url.match(/[?&]days=/))
this.href = url.replace(/([?&]days=)[^&]*/, '$1'+days)
else
this.href = url + (url.indexOf('?') < 0 ? '?':'&') + 'days=' + days
return true
}
function sortWatchlist(e){
e.preventDefault()
if (alreadySorted) return alert(mm.sortDone)
var H4s = $content.find('h4'), dayDiv, rows, h, i, j, pgname, el, step, last
//sort all days separately
for (var h=0; h<H4s.length; h++){
//get UL or DIV immediately after H4
dayDiv = H4s[h]
while ((dayDiv=dayDiv.nextSibling) && (dayDiv.nodeName != 'DIV') && (dayDiv.nodeName != 'UL'));
//get WL rows, find their namespaces, calculate sortkeys
rows = $(dayDiv).find('a[href*="&action=history"]')
if (rows.length == 0) return
for (i = 0; i < rows.length; i++){
pgname = getTitleFromURL(rows[i].href)
ns = getNSFromTitle(pgname)
if (ns>0) pgname = pgname.substring(getNamespace(ns).length+1) //title w/o prefix
if (ns%2) ns-- //sort talk page as if it was a base page
rows[i].sortkey = zzz(ns) + ':' + pgname //assign custom tag attribute: namespace+title
}
//sort rows array
rows.sort(function(a,b){
if (a.sortkey > b.sortkey) return 1
else if (a.sortkey < b.sortkey) return -1
else return 0
})
//sort rows in HTML
for (i=0; i<rows.length; i++){ //move rows to the dayDiv bottom
el = last = rows[i]
if (el.parentNode.nodeName == 'LI') //non-enhanced watchlist
el.parentNode.parentNode.appendChild(el.parentNode) //move to bottom
if (el.parentNode.nodeName == 'TD'){ //new enhanced WL, with tables
while ((el=el.parentNode) && el.nodeName != 'TABLE');
if (!el) continue
do{ //move table bottom, also taking next (hidden) DIV
step = el.nextSibling; el.parentNode.appendChild(el); el = step
}while (el && el.nodeName != 'TABLE')
}else{ //old enhanced WL, in case new is reverted
//find last row element , which is usually BR
while ((last=last.nextSibling) && last.nodeName!='BR');
if (!last) continue //just in case, this should not happen
//move to next DIV (if present), containing (collapsed) list of changes with "enhanced RC"
if ((step=last.nextSibling) && step.nodeName == '#text') last = step //Firefox insists on '\n' being here
if ((step=last.nextSibling) && step.nodeName == 'DIV') last = step
//now get to the 1st element, stopping just before <br> or <div>
while ((step=el.previousSibling) && step.nodeName!='BR' && step.nodeName!='DIV') el = step
//move all row elements one by one to the bottom of the day div
do { step=el.nextSibling; dayDiv.appendChild(el) } while (el != last && (el=step))
}
}
}//for
alreadySorted = true
}
function expandWatchlist(e){
e.preventDefault()
var i = 0, sp, state = $('#mw-rc-openarrow-0')[0].style.display
while (sp=document.getElementById('mw-rc-openarrow-'+(i++).toString()))
if (sp.style.display == state) $(sp.firstChild).click()
}
function addUnwatchLinks(e){
if (e) e.preventDefault()
if (alreadyAddedUnwatch) return alert(mm.unwatchDone)
$content.find('a[href*="&action=history"]').each(function(i, lnk){ appendXLink(lnk) })
alreadyAddedUnwatch = true
}
function appendXLink(lnk){
var x = $j('<a class=aj-unwatch style="font-size:smaller" title="'+mw.msg('unwatch')+'" />')
.attr('href', lnk.href.replace(/&curid=\d+/,'').replace(/action=history/,'action=unwatch'))
.click(ajaxUnwatch)
lnk = $(lnk)
if (isEnhanced) lnk.parent().prepend( x.append('(x)'), ' ' )
else lnk.after( ' | ', x.append('x') )
}
function toggleXLink(e){ //add (x) when clicking on timestamp
if ( ! $(e.target).filter('td.mw-enhanced-rc').length ) return
var tbl = $(e.target).parents('table.mw-enhanced-rc')
var x = tbl.find('a.aj-unwatch')
if (x.length) x.remove()
else appendXLink(tbl.find('a[href*="&action=history"]')[0])
}
function ajaxUnwatch(e) {
if (inProgress) return false
inProgress = getTitleFromURL(this.href)
timeoutID = window.setTimeout( function() {inProgress = null}, 10000 )
//call server
var req = { action:'watch', format:'json', title:inProgress }
if (/&action=unwatch/.test(this.href)) req.unwatch = ''
$.getJSON( '/w/api.php', req, showUnwatch)
return false
}
function showUnwatch (response) {
if (timeoutID) window.clearTimeout(timeoutID)
response = response.watch
var name = inProgress, state, pg
inProgress = null
if (response.watched !== undefined) state = false
else if (response.unwatched !== undefined) state = true
else return //unrecognized response
//find the full name of "other page"
var ns = getNSFromTitle(name)
var name2 = name
if (ns > 0) name2 = name2.substring(getNamespace(ns).length+1) //remove old prefix
if (ns % 2) ns--; else ns++ //switch to "other" namespace
if (ns > 0) name2 = getNamespace(ns) + ':' + name2 //add new prefix
//now mark all rows that are either name or name2
$content.find('a.aj-unwatch').each(function(i, lnk){
pg = getTitleFromURL(lnk.href)
if (pg != name && pg != name2) return
lnk.title = mw.msg(state?'watch':'unwatch')
$(lnk).parent().find('a').css('text-decoration', state ? 'line-through' : '')
lnk.href = lnk.href.replace(/&action=\w+/, '&action='+ (state?'watch':'unwatch'))
})
}
var maximizeCSS
function maximize(e){
if (e) e.preventDefault()
if (!maximizeCSS) maximizeCSS = mw.util.addCSS('\
div#siteNotice, #siteSub, #contentSub, fieldset#mw-watchlist-options,\
div.mw-rc-label-legend, #mw-fr-watchlist-pending-notice {display:none}\
h1#firstHeading {font-size:12pt} .wl-maximized {display:inline !important}')
else maximizeCSS.disabled = !maximizeCSS.disabled
document.cookie = 'wlmax='
+ (!maximizeCSS.disabled ? '1' : '0;expires=' + (new Date()).toGMTString() + ';;')
}
this.maximize = maximize
//common functions; need 'namespace' select element
function getNSFromTitle(title){ //returns namespace number
var i = title.indexOf(':')
if (i == -1) return 0
var prefix = title.substring(0,i+1) //including ':'
for (i=2; i < namespace.options.length; i++)
if (namespace.options[i].text+':' == prefix)
return i-1
return 0 // ':' was just a part of the title
}
function getNamespace(ns){ //returns namespace name
if (ns==0) return ''
else return namespace.options[ns+1].text
}
function getTitleFromURL (url){ //gets 'title=' part from a link
var ma = url.match(/(&|\?)title=([^&]+)/)
if (ma) return decodeURIComponent(ma[2]).replace(/_/g,' ')
else return ''
}
function zzz(s){ // 5 -> 005
s = s.toString()
if (s.length==1) return '00'+s
else if (s.length==2) return '0'+s
else return s
}
}//obj
if (wgCanonicalSpecialPageName == 'Watchlist' && wgAction == 'view') {
PngFixDisabled = true //enwiki
if (document.cookie.indexOf('wlmax=1') != -1) WLScript.maximize()
addOnloadHook(WLScript.onLoad)
}