MediaWiki:Script/LastContribs.js
Перейти к навигации
Перейти к поиску
Возможно, этот код документирован.
var script = {
name: 'LastContribs',
page: 'ТП:Персональные скрипты/Редактированные страницы'
}
var lastContribs = new function(){
// Localization
var msg={
legend:'Pages edited by user',
info:'Edits checked: $1, pages found: $2.',
next:'Request 50 next edits (200 | 500)'
}
if (wgUserLanguage=='ru') msg={
legend:'Страницы, которые правил участник',
only:'только',
articles:'статьи',
Find:'Найти',
Show:'Показывать',
unchanged:'неизменённые',
patrolled:'патрулированные',
Page:'Страница ',
'Number of edits':'Количество правок участника',
'Following changes': 'Последующие изменения ',
'No edits found': 'Таких правок не найдено',
info:'Просмотрено правок: $1, найдено страниц: $2.',
next:'Запросить 50 следующих правок (200 | 500)'
}
function mm(txt){ return msg[txt] || txt }
var pages //obj: current set of pages with their info
var known //obj: known[pageid]==5 means page was edited 5 times by this user
var ucLimit, ucContinue //parameters for api request
var editsN //number of requested edits
this.start = function (){ //createForm
appendCSS('\
#output {position: absolute; left:0; top:0; width:98%; min-height:95%;\
padding:0 1em; background:white; font-size:small}\
td.time {text-align:right}\
td.edits {text-align:right; font-size:90%}\
span.one {opacity:0.4}\
tr.patrolled td.page {background:#f0f5f0}\
span.sort {display:none}\
span.more {color:#0645AD; cursor:pointer}\
select {vertical-align:middle} th {font-weight:normal}\
#dialog {margin-top:0}')
$(document.body).children().hide()
var ns = parseInt(getURLParamValue('ucnamespace') || getURLParamValue('ns') || 0)
$('<div id=output>\
<fieldset id=dialog><legend>'+mm('legend')+'</legend>\
<form style="display:inline">'+ wgFormattedNamespaces[2]+
':<input name=ucuser size=15 value="'+(wgUserName||'')+'"> '+
checkbox('ucnamespace', mm('only') + ' ' +
(ns ? '«'+wgFormattedNamespaces[ns]+':»' : mm('articles')),
'value='+ns+' checked') + ' '+
'<input type=submit value="'+mm('Find')+'">'+
'</form><span style="border-left:1px solid gray; margin:0 2em" />'+
mm('Show') + ': ' +
checkbox('same', mm('unchanged'), 'checked') + ' ' +
checkbox('patrolled', mm('patrolled'), 'checked') +
'<input type=hidden name=uclimit value=50>' +
'</fieldset>\
<div id=messages style="float:right; margin:1em 0 0 1em; background:#f5f5f5; padding:5px" />\
<div id=results />\
</div>').click(onClick).appendTo(document.body)
$('#same, #patrolled').click( function (){
toggleCSS('tr.'+this.id+'{display:none}', this.checked)
})
UrlToForm('#dialog')
displayHelp() //temporary disabled: description page not ready
$('#dialog form').submit(formSubmit)
}//start
function formSubmit(e){
if (! $(this).find('input[name=ucuser]').val()) return false
$('#messages').empty()
$('#results').empty()
known = {}; ucContinue = ''; editsN = 0
ucLimit = $('#dialog input[name=uclimit]').val()
//simulate(); displayResult(); return false
requestContribs()
return false
}
function onClick(e){
var el = e.target
switch (el.className){
case 'page': //add history link on click
$('table#contribs span.hist').remove()
$('<span class=hist>(<a href="' +
$(el).find('a').attr('href')+'?action=history">h</a>)</span>')
.css('float','right').appendTo(el)
break
case 'thtime':
toggleCSS('td.time span.sort {display:inline}\
td.time span.ago {display:none}')
break
}
}
function requestContribs(){
editsN += parseInt(ucLimit)
spinner2('#dialog form')
api('&list=usercontribs&ucprop=ids|timestamp|size&ucstart='+ucContinue
+ '&uclimit='+ucLimit + '&'+$('#dialog form').serialize(),
recvContribs
)
}
function recvContribs(data){ //received user contribs
ucContinue = data['query-continue'] ? data['query-continue'].usercontribs.ucstart : ''
//check for empty results
if (!(data=data.query) || !(data=data.usercontribs))
return displayResult('Server returned empty data')
if (data.length == 0)
return displayResult(mm('No eidts found'))
//find pages we haven't seen before
pages = {}
var list = []
$.each(data, function(i,pg){
if (known[pg.pageid]) return known[pg.pageid]++
known[pg.pageid] = 1; list.push(pg.pageid)
pages[pg.pageid] = {revid:pg.revid, timestamp:pg.timestamp, size:pg.size}
})
//prepare to display results
$('#output').ajaxStop(function(){
$('#output').off('ajaxStop')
displayResult()
})
//request the last revision of each page, 50 pageids at a time
while (list.length > 0)
api({prop:'revisions', pageids: list.splice(0,50).join('|'),
rvprop:'ids|user|size|timestamp|flagged'}, recvLastRevisions)
}
function recvLastRevisions(data, status){ //add title and last revision to each page in pages{}
$.each(data.query.pages, function(i,v){
if (!pages[v.pageid])
dispMsg('Could not find '+v.pageid+' '+v.title+' in pages{}')
pages[v.pageid].title = v.title
pages[v.pageid].ns = v.ns
pages[v.pageid].last = v.revisions[0]
})
}
function displayResult(txt){
spinner2()
if (txt) return $('#results').text(txt)
if ($('table#contribs').length == 0){ //createTable
$('#results').html('\
<div class=info />\
<table class=wikitable id=contribs><tr>\
<th class=thtime title="hh:mm or dd,hh">'+
outputIcon('2/26/Clock_simple.svg',15,'class=thtime')+'</th>\
<th>' + mm('Page')+' </th>\
<th title="'+mm('Number of edits')+'"></th>\
<th>' + mm('Following changes')+' </th></tr></table>\
<div class=info />')
ts_makeSortable($('table#contribs')[0])
}
//outputIcon('e/ec/Btn_edit.gif','','title="'+mm('Number of edits')+'"')
var clss, arr=[], htm='', pgid, pg
//update number of edits on old rows
$('table#contribs tr').each(function(i){
if (i==0) return
$(this).find('td.edits').html( showEdits($(this).attr('pg')) )
})
//sort by timestamp using temp array
$.each(pages, function(pgid,v){ arr.push([pgid,v.timestamp]) })
arr.sort(function(a,b){return a[1]>b[1]?-1:1})
//add rows to the table
$.each(arr, function(i,v){
pgid = v[0]
pg = pages[pgid]
clss = pg.revid==pg.last.revid ? 'same' : 'changed'
if (pg.last.flagged) clss += ' patrolled'
htm += '<tr class="'+clss+ '" pg='+pgid+'>'+
'<td class=time><span class=sort>'+pg.timestamp+'</span><span class=ago>'+
outputPeriodInHours(serverTime-parseTimestamp(pg.timestamp))+'</span></td>'+
'<td class=page><span class=sort>'+pad0(pg.ns,3)+'</span>'
+ outputPageLink(pg.title)+'</td>'+
'<td class=edits>'+showEdits(pgid)+'</td>'+
'<td class=lastedit><span class=sort>'+pg.last.timestamp+'</span>'
if (/changed/.test(clss)) htm +=
'<span'+(pg.revid==pg.last.parentid ? ' style=visibility:hidden':'')
+'> <b>…</b> </span>'+ //…
outputUserLink(pg.last.user)+
'<span style="float:right; margin-left:1em">' + outputIntLink(
'oldid='+pg.revid+'&diff='+pg.last.revid,
pg.last.size - pg.size,
'class='+outputDiffClass(pg.last.size - pg.size))+
'</span>'
htm += '</td></tr>'
})
$('table#contribs').append('<tbody>'+htm+'</tbody>')
$('div.info').html(
mm('info').replace('$1', editsN).replace('$2', arr.length) +
(ucContinue ? ' '+mm('next').replace(/\d+/g, '<span class=more>$&</span>') :'')
)
$('span.more').click(function(){
ucLimit = $(this).text()
requestContribs()
})
}
function showEdits(pgid){
var nn = known[pgid]
if (nn == 1) nn = '<span class=one>'+nn+'</span>'
return nn
}
} //lastContribs
/*start*/
var maxTitleLength = 80
if( /^Тептар:Персональные_скрипты/.test(wgPageName) ) $(lastContribs.start)
/* small API library */
//initialization
var serverTime = ''
var $debug = getURLParamValue('debug')
$(function(){
$().ajaxSuccess( function(e, aj){ //get server time from other requests
serverTime = parseServerTime(aj.getResponseHeader('Date'))
})
$.ajaxSetup({ cache:false })
})
function api(data, callback){
if (typeof data=='object') data = $.param(data)
var url = wgScriptPath+'/api.php?action=query&'+data
//display request URL
if (window.$debug) dispMsg($('<a>').text(/&(list|prop)=(\w+)/.exec(url)[2])
.css('margin-right','1em').attr('href',url))
//request
$.getJSON(url+'&format=json', function(d,s){
if (d.error) dispMsg('API error: '+d.error.code+': '+d.error.info, true)
if (d.warnings) dispMsg('API warning: '+dumpJSON(d.warnings).replace(/\n/g,'<br>'), true)
callback(d,s)
})
}
function dispMsg(htm, isErr){ //div#messages should exist
if (isErr) htm = '<p class=error>'+htm+'</p>'
$('#messages').append(htm,'<br />').show()
}
function dumpJSON(o){
var s = ''
for (var k in o)
if (typeof o[k] == 'object') return k+':'+dumpJSON(o[k])
else s+=o[k]+'\n'
return s
}
function checkbox(name, text, attr){
return '<input type=checkbox name='+name+' id='+name +
(attr?' '+attr:'')+'><label for='+name+'>'+text+'</label>'
}
function getURLParamValue(name){
var m = RegExp('[&?]'+name+'=([^&#]*)').exec(document.URL)
if (m && m.length > 1) return decodeURIComponent(m[1])
return null
}
function UrlToForm(obj){ //autofill form from URL
obj = $(obj)
var val
obj.find('input').add(obj.find('select')).each(function(t,el){
val = getURLParamValue(el.name)
if (val==null) return
switch (el.type){
case 'radio':
obj.find('input [type=radio][value="'+val+'"]').attr('selected',true)
break
case 'checkbox':
if (el.value==val || val=='on') if (!el.checked) el.click()
else if (val=='' || val=='off') if (el.checked) el.click()
else el.value = val
break
default: //text, select
el.value = val.replace(/_/g,' ')
}
})
}
function formToUrl(obj){
obj = $(obj)
var url = '&'+obj.serialize().replace(/\+/g,'_')
obj.find('input:checkbox:not(:checked)').each( function(t,el){
url += '&' + el.name + '=' //also "remember" unchecked checkboxes
})
return url
}
function displayHelp(e){
if (!e) //called from Script just to add help link to #dialog
$(outputIcon('/4/44/Help-browser.svg', 25,
'alt=help style="cursor:pointer;float:right"'))
.click(displayHelp).appendTo('#dialog')
else if ($('#help').length !=0)
$('#help').remove()
else {
var url = wgArticlePath.replace(/\$1/,'') + wgPageName +
'?run='+script.name + formToUrl('#dialog')
var hlp = $('<div id=help style="position:absolute;top:30px; right:70px;\
border:2px outset gray; background:white; padding:1em"><div id=page /><br><hr>\
Подробнее см. ' + outputPageLink(script.page) + '<br><hr>\
Ссылка на скрипт с текущими параметрами: <a href="'+url+'">'+script.name+'</a>')
.appendTo(document.body).click(function(){$(this).remove()})
loadTemplate('#page', script.page)
}
}
function loadTemplate(sel, page){
$.get(wgScriptPath+'/api.php?action=parse&format=xml&prop=text&text={\{'
+encodeURIComponent(page)+'}}', function(data){
$(sel).html($(data).find('text').text())
})
// https://massarn.com/w/api.php?action=parse&text={{user%20talk:Alex%20Smotrov}}&prop=text
}
function parseServerTime(ts){ //"Tue, 29 Jan 2008 22:32:21 GMT" -> date
if (!ts) return null
var m = ts.match(/(\d\d?) ([A-Z][a-z][a-z]) (\d\d\d\d) (\d\d):(\d\d):(\d\d)/)
if (m){
var d = new Date(); d.setYear(m[3]); d.setDate(m[1]);
d.setMonth('janfebmaraprmayjunjulaugsepoctnovdec'.indexOf(m[2].toLowerCase()) / 3)
d.setHours(m[4]); d.setMinutes(m[5]); d.setSeconds(m[6])
}else
var d = new Date(ts.substring(0, ts.length-4)) //cut "GMT" part
return d
}
function parseTimestamp(ts){ //20071226220605 or 2008-01-26T06:34:19Z -> date
if (!ts) return null
ts = ts.replace(/\D/g,'')
var d = new Date()
d.setYear(ts.substring(0,4))
d.setMonth(ts.substring(4,6)-1)
d.setDate(ts.substring(6,8))
d.setHours(ts.substring(8,10))
d.setMinutes(ts.substring(10,12))
d.setSeconds(ts.substring(12,14))
return d
}
function outputPeriodInHours(mlsec){ //milliseconds -> "2:30" or 5,06 or 21
var mm = Math.floor(mlsec/60000)
var hh = Math.floor(mm/60); mm = mm % 60
var dd = Math.floor(hh/24); hh = hh % 24
if (dd) return dd + (dd<10?'<small>,'+pad0(hh,2)+'</small>':'')
else return '<small>'+hh + ':' + pad0(mm,2)+'</small>'
}
function outputIntLink(link, name, attr){
return '<a href="' + wgScript + '?' + link + '" '+ (attr||'') + '>'+name+'</a>'
}
function outputPageLink(page, name){
name = name || page
if (window.maxTitleLength && name.length > maxTitleLength)
name = name.substring(0,maxTitleLength)+'…'
return '<a href="' + wgArticlePath.replace(/\$1/,'')
+ encodeURI(page).replace(/\?/g,'%3F').replace(/%20/g,'_')
+ '" title="'+page.replace(/"/g,'"')+'">' + name + '</a>'
//encodeURIComponent(page).replace(/%2F/g,'/')
}
function outputUserLink(user){
return outputPageLink('Special:Contributions/'+user, user)
}
function outputDiffClass(n){
return 'mw-plusminus-'+ (n>0 ? 'pos' : (n<0?'neg':'null'))
}
function outputIcon(src, size, attr){ //returns "<img ...>"
if (size) src = 'thumb/'+src+'/'+size+'px-'+src.split('/')[2]+'.png' //for svg
return '<img src="//upload.wikimedia.org/wikipedia/commons/'
+ src + '" ' + (attr||'')+'>'
}
function pad0(v, len){ v = v.toString(); while (v.length < len) v = '0'+v; return v } // 6 -> '06'
var toggleCSSObj = {}
function toggleCSS(css, isDisabled){
if (!toggleCSSObj[css]) toggleCSSObj[css] = addCSS(css)
toggleCSSObj[css].disabled = isDisabled
}
function addCSS(c){ var s = appendCSS(c); return s.sheet || s } // Safari compat
function spinner2(sel){
if (!sel) $('img.spinner').remove()
else $(sel).append('<img class=spinner style="margin-left:1em" src="'+stylepath
+'/common/images/spinner.gif" alt="..." title="..." />')
}