//var rcDataSortable = true
if( /Watchlist|Recentchanges/.test(mw.config.get('wgCanonicalSpecialPageName')) ){
$.when(
$.ready,
mw.loader.using(['mediawiki.cookie', 'mediawiki.util']),
$.getScript(mw.config.get('wgScript') + '?title=User:Js/apl.js&action=raw&ctype=text/javascript')
)
.then(rcExtraData);
if( window.rcDataSortable ) mw.loader.load('jquery.tablesorter')
}
function rcExtraData(){
var pgTitle = {},
rcQuery = { list:'abuselog', afllimit:100, aflprop:'ids|filter|user|title|action|result|timestamp' }
switch( mw.config.get('wgCanonicalSpecialPageName') ){
case 'Watchlist': //add request for new pages
//calculate from parameter from URL &days=
var from = /[?&]days=([\d\.]+)/.exec( document.URL )
if( !from || $('#namespace').val() ) return false //do not work on namespace-filtered watchlist
var d = new Date() //and then try to ajust to server time using time from [[MediaWiki:Wlnote]]
var ma = $('#mw-watchlist-options').text().match(/(\d\d):(\d\d),[^\d]* (\d\d?)/)
if(ma) { d.setUTCDate(ma[3]); d.setUTCHours(ma[1]); d.setUTCMinutes(ma[2]); d.setUTCSeconds(0) }
var dateDays = new Date(d - from[1] * 24 * 3600 * 1000);
// Код этого блока был добавлен позже
var fromParam = /[?&]from=([\d\.]+)/.exec( document.URL );
var dateFromMatches = fromParam && fromParam[1].match(/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/);
if (dateFromMatches) {
var year = Number(dateFromMatches[1]);
var month = Number(dateFromMatches[2]) - 1;
var day = Number(dateFromMatches[3]);
var hours = Number(dateFromMatches[4]);
var minutes = Number(dateFromMatches[5]);
var seconds = Number(dateFromMatches[6]);
var dateFrom = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
}
var date = dateFrom > dateDays ? dateFrom : dateDays;
from = date.toISOString();
rcQuery.aflend = from
//also request new pages, unless disabled
if( !window.rcDataNoNewPages ){
rcQuery.list += '|recentchanges'
$.extend(rcQuery, { rctype: 'new', rclimit: 50, rcshow: '!bot', rcend: from,
rcprop: 'title|user|parsedcomment|sizes|flags|redirect' })
}
//also request some usercontribs
if( !window.rcDataNoContribs ){
var users = wlContribs()
//mw.util.$content.find('a[href*=":Contributions/"]').hover(hoverContribLink)
mw.util.$content.delegate('a[href*=":Contributions/"]', 'mouseover', hoverContribLink)
if( users ){
rcQuery.list += '|usercontribs'
$.extend(rcQuery, { ucuser: users, uclimit: 100, ucend: from,
ucprop:'ids|title|timestamp|parsedcomment' } )
}
}
break
case 'Recentchanges':
//extra feature: make main tab on top to be "only new" link
var url = mw.util.$content.children('fieldset.rcoptions').children('hr').prev('a').attr('href')
var tab = $('#ca-special, #ca-nstab-special').eq(0).find('a')
if( url && tab.length ) tab.attr( 'href', url ).text( tab.text() + ' Δ' )
//calc "from"
var from = /&from=(\d+)/.exec(document.URL)
if( !from ) return
rcQuery.aflend = from[1]
break
default: return //some other special page
}//switch
mw.util.addCSS('\
div.apiresults table td {padding: 0 1em}\
div.apiresults table th {font-weight:normal; font-size:smaller}\
div.apiresults table {border-spacing:0}\
div.afl table {background-color: #f2f2f2}\
div.afl td.title {font-size:smaller}\
div.afl tr.odd {background-color:#eaeaea}\
div.uc table {background-color: #fff5f5}\
td.parsedcomment {font-size:85%}\
td.title.redirect a {color:gray}\
tr.touched td.afl-result {font-weight:bold}\
div.watched {background:#ccf}\
span.wx {cursor:pointer; margin-right:1em}\
')
$.extend(rcQuery, window.rcDataRequest) //possible user-defind config
apl.queryAPI(rcQuery, receiveData)
return
//--------------------------- Functions
function receiveData(resp){
//prepare output div
$('<div id="js-output" />').appendTo( mw.util.$content )
//notify user if our time was wrong
var timeErr = ( new Date() - wgServerTime ) / 1000
if( timeErr > 60 ) showMsg('Your time is different from server time: ' + timeErr + ' sec')
//show data
if( resp.error ) showMsg('API responded with ' + resp.error.code + ':' + resp.error.info + '<br />')
if( !resp || !(resp=resp.query) ) return showMsg('<span style="color:gray">--</span>')
$('#js-output').append(
'<hr style="clear:both" />',
showAbuseLog( resp.abuselog ),
'<hr style="clear:both" />',
showContribs( resp.usercontribs ),
$('<span style="cursor:pointer; float:left; margin-right:1em"\
id=users-button title="watch contribs">></span>')
.click(editContribs)
)
if( window.markBlocked ) markBlocked('#js-output') // for ru:MediaWiki:Gadget-markblocked.js
$('#js-output').append(
'<hr style="clear:both" />',
showNewPages( resp.recentchanges )
)
if( window.rcDataSortable ) setTimeout("$('#js-output table').tablesorter()", 1000)
requestTitlesProperties()
}
function showAbuseLog(log){
if( !log || log.length == 0 ) return ''
var lastShown = mw.cookie.get('aflast') //avoid showing the same events over and over
//group by user
var kk = 0, ii, tmp
do{ //check all events below and move events with the same user next to this one
for( ii=kk+1; ii<log.length; ii++)
if( log[kk].user == log[ii].user ){
kk++;
if( kk != ii){ tmp = log[kk]; log[kk] = log[ii]; log[ii] = tmp } //swap
}
} while( ++kk < log.length )
var item, result, tbody = '', isOdd = true
for( ii=0; ii<log.length; ii++ ){
item = log[ii]
if( lastShown && item.id <= parseInt(lastShown[1]) ) continue
rememberTitle(item, 'afl' + ii)
result = {'':'✓', tag: '✓', warn: 'w', disallow: 'x'}[item.result] || '?'
if( ii > 0 && item.user != log[ii-1].user ) isOdd = ! isOdd //change background with another user
tbody += '<tr id=afl' + ii
+ ' class="' + result + ' ' + item.action + (isOdd?' odd':'') + '">'
+ apl.outputCell(item, 'title')
+ apl.outputCell(item, 'user')
+ '<td class=filter>' + apl.outputPage('Special:AbuseFilter/'+item.filter_id, item.filter) + '</td>'
+ '<td class="afl-result">' + apl.outputPage('Special:AbuseLog/'+item.id, result) + '</td>'
+ '<td class=ago>' + Math.round( (wgServerTime - apl.parseTimestamp(item.timestamp) )/1000)+'</td>'
+ '</tr>'
}
mw.cookie.set('aflast', log[log.length-1].id)
return createTable(['title','user','filter','','ago'], tbody, 'afl')
}
function showContribs(log){
if( !log || log.length == 0 ) return ''
var item, result, tbody = '', isOdd = true
for( ii=0; ii<log.length; ii++ ){
item = log[ii]
// if( ii > 0 && item.user != log[ii-1].user ) isOdd = ! isOdd //change background with another user
tbody += '<tr id=uc' + ii + '>'
+ apl.outputCell(item, 'title')
+ apl.outputCell(item, 'user')
+ '<td>' + apl.outputLink('diff=' + item.revid, 'Δ') + '</td>'
+ '<td>' + Math.round( (wgServerTime - apl.parseTimestamp(item.timestamp) )/1000)+'</td>'
+ apl.outputCell( item, 'parsedcomment' )
+ '</tr>'
}
return createTable(['title','user','Δ','ago',''], tbody, 'uc')
}
function hoverContribLink(e){
$('.watcher').remove()
var aa = $(e.target)
var usr = /Contributions\/([^?&]+)$/.exec( aa.attr('href'))
if( e.target.nodeName != 'A' || !usr ) return
usr = decodeURIComponent(usr[1]).replace(/_/g, ' ')
//var pos = aa.offset()
var watcher = $('<div class=watcher title="watch contribs"\
style="position:absolute; border:1px dotted gray; cursor:pointer" />')
.css({top: aa.offset().top, left:aa.offset().left-10, width:10, height:10 })
.appendTo('body')
.toggleClass('watched', wlContribs(usr) )
.mouseleave( function(){ $(this).remove() } )
.click(function(){
$(this).toggleClass('watched')
wlContribs( usr, $(this).hasClass('watched') )
})
}
function editContribs(e){
$('#users-edit').remove()
var btn = $('#users-button')
var state = parseInt( btn.attr('state') || '0' )
if( e != false ) state++ //switch to next state
var users = (wlContribs() || '').split('|')
//if( !users ) return
var uList = $('<div id=users-edit />').insertAfter('#users-button')
switch( state ){ //
case 1: //edit mode
var htm = ''
for( var i=0; i<users.length; i++)
if(users[i] )
htm += apl.output(users[i], 'user')
+ ' <span class=wx user="' + users[i] + '">x</span> '
if( !htm ) htm = '--'
uList.html(' ' + htm)
uList.find('.wx').click(function(){
wlContribs( $(this).attr('user'), false )
editContribs(false)
})
break
case 2: //raw mode
uList.append(
$('<input type=button value=Save />')
.click(function(){
wlContribs( mw.html.escape($('#users-list').val()).replace(/\n/g,'|'), 'list' )
$(this).closest('div').remove()
})
,
mw.html.escape(
$('<textarea id=users-list rows=' + (users.length+1) +'/>')
.val( users.join('\n') )
)
)
break
}
btn.attr('state', state % 3)
}
function showNewPages(rcc){
if( !rcc || rcc.length == 0 ) return ''
var item, comm, ma, tbody = ''
for( var ii=0; ii< rcc.length; ii++ ){
item = rcc[ii]
//simplify "new page" comment
comm = item.parsedcomment
if ( ma = comm.match(/^<a[^>]+>←<\/a> Новая страница: «(.*)»$/) )
comm = '<span class="page-quote">«' + ma[1] + '»</span>'
else if ( ma = comm.match(/^<a[^>]+>←<\/a> <a[^>]+>Перенаправление<\/a> на «(.*)»(.*)$/) )
comm = '→ ' + ma[1] + ma[2]
else
comm = '<i>' + comm + '</i>'
//add to table
tbody += '<tr id=np' + ii + '>'
+ apl.outputCell( item, 'title' )
+ apl.outputCell( item, 'user' )
+ apl.outputCell( comm, 'parsedcomment' )
+ apl.outputCell( item, 'newlen' )
+ '</tr>'
}
return createTable(['new page', 'user', '', 'size'], tbody, 'npg', 2)
}
// ------------------------ 2nd API request for title properties to:
//1) mark red links 2) make bold filter events when title was "touched" - this usually means the page was recently edited
function rememberTitle(it, domId){ //"save" title into our object
if( ! pgTitle[it.title] ) pgTitle[it.title] = []
pgTitle[it.title].push( {id: domId, ts: it.timestamp } )
}
function requestTitlesProperties(){
//reques titles properties, to mark red links
var arr = []
for (var ttl in pgTitle) arr.push(ttl)
while( arr.length > 0 ) //API only accepts 50 titles per request
apl.queryAPI( { prop: 'info', titles: arr.splice(0,50).join('|') }, showTitlesProperties )
}
function showTitlesProperties(pages){
pages = apl.getChild(pages, 'query.pages')
var pg, savedTitle, tr
for (var id in pages){
pg = pages[id]
savedTitle = pgTitle[ pg.title ] //[ { id:domId, ts: timestamp }, ... ]
for (var i=0; i<savedTitle.length; i++){
tr = $('#'+savedTitle[i].id)
if( typeof pg.missing == 'string' )
tr.find('td.title').find('a').addClass('new')
else if( pg.touched >= savedTitle[i].ts ) //most likely was edited after triggering
tr.addClass('touched')
}
}
}
// ------------------------ AUX functions
function showMsg(txt){
$('#js-output').append(txt + '<br />')
}
function createTable(THs, tbody, clss, sortByColumn){
if( !tbody ) return ''
else return '<div class="apiresults ' + (clss||'') + '">'
+ '<table class=sortable><tr><th>' + THs.join('</th><th>') + '</th></tr>'
+ tbody + '</table></div>'
}
}
function wlContribs(user, action){
var list = mw.cookie.get('wco') || ''
if( !user ) return list
switch( action ){
case undefined: return list.indexOf(user) != -1
case false: list = list.replace(user,''); break
case true: list += '|' + user; break
case 'list': list = user; break
}
mw.cookie.set(
'wco',
list.replace(/\|\|/,'|').replace(/^\||\|$/g,''),
{expires: 7}
)
}