Jump to content

User:Js/watchlist.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Js (talk | contribs) at 18:53, 21 February 2011 (fixing (x) placement and $content for other skins; (x) can be added by clicking on timestamp; more jQuery syntax). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
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.
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() {

 PngFixDisabled = true 

 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...
 }

 isEnhanced = $content.find('ul.special').length == 0
 if (isEnhanced) $content.click(toggleXLink)

 var insAt = $('#mw-watchlist-options').find('a[href$=&days=7]').next().next() //find insertion point: after "days all"
 if (!insAt.length) insAt = $('#mw-watchlist-options').find('hr').last()
 
 //append "only new" link
 var lnk = addLnk(onlyNewEntries, mm.onlynew, mm.onlynewTip)
 .attr('id', 'listSince').mousedown(onlyNewEntries) //middle clicks too

 //append "maximized mode" links
 $('#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}')

 //append other links
 addLnk(addUnwatchLinks, 'x' , mm.unwatchTip)
 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){
 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 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') )
   //experimental
   /*
   lnk.attr('title', lnk.text()).text('Ω')
   lnk = lnk.prev()
   lnk.attr('title', lnk.text()).text(lnk.text().replace(/[^\d]+/,'Δ'))
   */
}

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') {
  if (document.cookie.indexOf('wlmax=1') != -1) WLScript.maximize()
  addOnloadHook(WLScript.onLoad)
}