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
mm = {
sort:'Sort...',
sortTitle:'Sort titles by even namespace number, then by page title',
sortDone:'Watchlist changes already sorted',
unwatchTitle:'Add (x) quick unwatch links',
unwatchDone:'Use (x) links to quickly unwatch pages',
onlynew: 'Only new',
onlynewTitle: 'Show changes since this page was loaded',
expandAll: 'Expand/hide all',
fullPage: 'Hide/Show interface'
}
this.onLoad = function() {
PngFixDisabled = true
namespace = document.getElementById('namespace')
if (!namespace) return
$content = $('#bodyContent')
//find insertion point
var insert = namespace.form
while (insert.previousSibling && insert.nodeName != 'BR') insert=insert.previousSibling
//append "only new" link
var lnk = addLnk('#', mm.onlynew, mm.onlynewTitle)
lnk.id = 'listSince'
lnk.onclick = lnk.onmousedown = onlyNewEntries // react to middle clicks too
//append "maximized mode" links
var head = document.getElementById('firstHeading')
lnk = lnk.cloneNode(true); lnk.className = 'wl-maximized'
lnk.onclick = lnk.onmousedown = onlyNewEntries
head.appendChild(lnk)
var rst = document.createElement('span'); rst.innerHTML = '↙'; rst.onclick = maximize
rst.className = 'wl-maximized'; rst.id = 'wl-restore'
appendCSS('.wl-maximized { margin-left:1em; display:none}\
#wl-restore {float:right; cursor:pointer; font-size:1.6em;\
border:1px outset gray; border-bottom:none; padding:0 2px}')
head.insertBefore(rst, head.firstChild)
//append other links
addLnk('javascript:WLScript.addUnwatchLinks()', 'x' , mm.unwatchTitle)
addLnk('javascript:WLScript.sortWatchlist()', mm.sort, mm.sortTitle)
if ($('#mw-rc-openarrow-0').length == 1)
addLnk('javascript:WLScript.expandWatchlist()', '±', mm.expandAll)
addLnk('javascript:WLScript.maximize()', '↗', mm.fullPage)
// function adds " | <link>" just before 'insert' element
function addLnk(url, text, tooltip){
var lnk = document.createElement('a')
lnk.href = url
lnk.appendChild(document.createTextNode(text))
lnk.title = tooltip || ''
insert.parentNode.insertBefore(document.createTextNode(' | '), insert)
insert.parentNode.insertBefore(lnk, insert)
return lnk
}
}
function onlyNewEntries() {
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
}
this.sortWatchlist = function(){
if (alreadySorted) return alert(mm.sortDone)
var H4s = $content[0].getElementsByTagName('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
}
this.expandWatchlist = function(){
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()
}
this.addUnwatchLinks = function() {
if (alreadyAddedUnwatch) return alert(mm.unwatchDone)
$content.find('a[href*="&action=history"]').each(function(i, lnk){
x = $j('<a class=unwatch title="'+mw.msg('unwatch')+'">x</a>')
.attr('href', lnk.href.replace(/&curid=\d+/,'').replace(/action=history/,'action=unwatch'))
.click(ajaxUnwatch)
lnk = $(lnk)
lnk.after(x).after(' | ')
})
alreadyAddedUnwatch = true
}
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.unwatch').each(function(i, lnk){
pg = getTitleFromURL(lnk.href)
if (pg != name && pg != name2) return
lnk.title = mw.msg(state?'watch':'unwatch')
lnk.parentNode.style.textDecoration = state ? 'line-through' : ''
lnk.href = lnk.href.replace(/&action=\w+/, '&action='+ (state?'watch':'unwatch'))
})
}
var maximizeCSS
function maximize(){
if (!maximizeCSS) maximizeInit()
else maximizeCSS.disabled = !maximizeCSS.disabled
document.cookie = 'wlmax='
+ (!maximizeCSS.disabled ? '1' : '0;expires=' + (new Date()).toGMTString() + ';;')
}
this.maximize = maximize
function maximizeInit(){
maximizeCSS = 'div#siteNotice, #siteSub, #contentSub,\
fieldset#mw-watchlist-options, div.mw-rc-label-legend {display:none}\
h1#firstHeading {font-size:12pt} .wl-maximized {display:inline !important}'
switch (skin){
case 'vector': maximizeCSS += '#p-logo{display:none} #footer {clear:both}\
#p-personal, #content, #footer {margin-left:0} #left-navigation {left:1.5em}\
#mw-panel {position:static !important; width:100% !important}\
#mw-panel div.portal {float:left; background:none}'; break
case 'monobook': case 'simple': case 'myskin': maximizeCSS += '\
#p-logo {display:none} #column-content {float:none; margin-left:0}\
#column-content #content {margin-left:2px}\
#column-one {position:static; padding-top: 10px}\
.portlet {float:left} #p-cactions {left:0} #footer {margin-left:0}'
break
}
maximizeCSS = appendCSS(maximizeCSS)
maximizeCSS = maximizeCSS.sheet || maximizeCSS
}
this.maximizeInit = maximizeInit
//common functions, some 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 simply a part of a 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') {
if (document.cookie.indexOf('wlmax=1') != -1) WLScript.maximizeInit()
addOnloadHook(WLScript.onLoad)
}