MediaWiki:Gadget-ondemand-arbcomVoting.js
JS-код ниже относится к скрытому гаджету ondemand-arbcomVoting. Связанный CSS-файл: MediaWiki:Gadget-ondemand-arbcomVoting.css. Связанный JSON-файл: MediaWiki:Gadget-ondemand-arbcomVoting.json. Он вызывается по умолчанию на страницах в категории Википедия:Страницы с гаджетом по требованию arbcomVoting.
После сохранения или недавних изменений очистите кэш браузера.
/*
* Скрипт для выборов в Арбитражный комитет (ВП:АК)
*
* Авторы: Kalan, Serhio Magpie, stjn
*/
$(function () {
// Не запускать без наличия контейнера
var containerEl = document.getElementById( 'voting-container' );
if ( containerEl === null ) {
mw.log.warn( 'ext.gadget.ondemand-arbcomVoting: Aborted because no voting container detected.' );
return;
}
/******* COMMON ******* */
function el(e) { return document.createElement(e) }
function tstamp(t) { return !t.getUTCFullYear() ? null : // Safari + Chrome
(t.getUTCFullYear()+":0"+(t.getUTCMonth()+1)+":0"+t.getUTCDate()+":0"+
t.getUTCHours() +":0"+ t.getUTCMinutes() +":0"+t.getUTCSeconds())
.replace(/:0?(\d\d)/g, '$1') }
function ch(o) { for (var i in o) { return o[i] } }
function getImgUrl( url ) {
return 'https:/upwiki/wikipedia/commons/'+ url;
}
/******* VARIABLES *******/
var data = require( './ondemand-arbcomVoting.json' );
var wgUserName = mw.config.get('wgUserName');
var wgNamespaceNumber = mw.config.get('wgNamespaceNumber');
var votes = {};
var saving = null;
var api;
var containerEl, statusEl, btnEl;
/******* MAIN *******/
function votingLength( date, days ) {
var result = new Date( date );
result.setDate( result.getDate() + days );
return result;
}
function votingStart() {
containerEl.className = 'active';
statusEl.innerHTML = data.strings.statuscriteria;
btnEl.setDisabled(true);
btnEl.$element.css( 'display', 'none' );
api.get({
'action': 'query',
'list': 'users',
'usprop': ['registration', 'editcount'],
'prop': 'revisions',
'rvprop': 'content',
'ususers': wgUserName,
'titles': data.config.voteexceptionspath
}).done(votingStartContinue);
}
function votingStartContinue(d) {
query = d.query;
pages = query.pages;
userinfo = query.users[0];
if ((// a valid voter must be a non-anonymous user
typeof userinfo === 'undefined' || typeof userinfo.missing !== 'undefined' ||
// who is not blocked
userinfo.blockedby ||
// whose editcount is at least data.config.criteria.count
userinfo.editcount < data.config.criteria.count ||
// and who is registered no later than data.config.criteria.registration
// "null" means "before 2005-12-29", user creation was not logged before then
(userinfo.registration !== null && userinfo.registration > data.config.criteria.registration)) &&
// exemptions
('\n' + ch(pages).revisions[0]['*'] + '\n').indexOf('\n* %\n'.replace('%', wgUserName)) == -1
) {
votingCriteriaFail();
} else {
votingContinue();
}
}
function votingCriteriaFail() {
statusEl.innerHTML = data.strings.criteriafail;
}
function votingContinue() {
statusEl.innerHTML = data.strings.loadingvotes;
// latest contributions in ns:4
api.get({
'action': 'query',
'list': 'usercontribs',
'ucnamespace': wgNamespaceNumber,
'uclimit': 500,
'ucuser': wgUserName,
'ucend': tstamp(data.config.start),
'ucdir': 'older',
'prop': 'revisions',
'rvprop': 'content'
}).done(votingDraw);
}
function onClick( e ) {
e.preventDefault();
var button = e.target;
var img = e.target.querySelector( 'img' );
var candidate = button.dataset.candidate;
var type = button.dataset.type;
var option = data.options[ type ];
// Change vote
var vote = type === 'support' ? 1 : -1;
votes[ candidate ].value = ( votes[ candidate ].value === vote ? 0 : vote );
var votedBefore = votes[ candidate ].orig === vote;
var pickedOption = votes[ candidate ].value === 1 ? 'undovote' : 'novote';
img.src = getImgUrl( option.icons[ pickedOption ] );
img.alt = pickedOption;
if ( votedBefore && pickedOption === 'undovote' ) {
img.alt = option.voted.replace( '%candidate%', candidate ) + option.restorevote;
}
img.title = img.alt;
}
function addVoteButton( candidate, type, didVote ) {
var option = data.options[ type ];
var voteType = didVote ? 'voted' : 'novote';
var button = document.createElement( 'button' );
button.setAttribute( 'type', 'button' );
button.setAttribute( 'data-candidate', candidate );
button.setAttribute( 'data-type', type );
button.addEventListener( 'onclick', onClick );
var img = document.createElement( 'img' );
img.alt = (
didVote
? option[ voteType ].replace( '%candidate%', candidate ) + option.undovote
: option.novote
);
img.title = img.alt;
img.width = img.height = 40;
img.src = getImgUrl( option.icons[ voteType ] );
button.appendChild( img );
return button;
}
function votingDraw(d) {
statusEl.innerHTML = '';
btnEl.setLabel(data.strings.savebutton);
btnEl.setDisabled(false);
btnEl.off('click').on('click', votingSave);
btnEl.$element.css( 'display', '' );
var query = d.query;
var co = query.usercontribs;
// retrieving votes, according to contributions, only latest ones are valid
for (i=0; i<data.candidates.length; i++) {
votes[data.candidates[i]] = {'orig':0, 'value':0};
}
for (var i=co.length-1; i>=0; i--) {
var m = co[i].title.indexOf(data.config.votepath) === 0;
if (m) {
m = co[i].title.match(/\/([+-])\/(.*?)$/);
}
if (m) {
votes[m[2]] = { 'orig' : m[1]=='+' ? +1 : -1, 'value': 0 };
}
}
// drawing
var div1 = el('div');
div1.id = 'voting-standard';
div1.innerHTML = data.strings.votinghelp;
var tab = el('table');
for ( var candidate in votes ) {
if ( candidate.match( /^_[srl]$/ ) ) {
continue;
}
var tr = el( 'tr' );
var th = el( 'th' );
th.setAttribute( 'scope', 'row' );
var lin = el('a');
lin.href = mw.util.getUrl( data.config.talkpath + candidate );
lin.target = '_blank';
lin.innerHTML = candidate;
lin.title = data.strings.questionsTooltip.replace( '%candidate%', candidate );
th.appendChild( lin );
tr.appendChild( th );
// Support button
var td1 = el( 'td' );
td1.appendChild( addVoteButton( candidate, 'support', votes[ candidate ].orig === 1 ) );
tr.appendChild( td1 );
// Oppose button
var td2 = el( 'td' );
td2.appendChild( addVoteButton( candidate, 'oppose', votes[ candidate ].orig === -1 ) );
tr.appendChild( td2 );
tab.appendChild( tr );
}
div1.appendChild(tab);
statusEl.innerHTML = data.strings.justbeforesave;
statusEl.parentNode.insertBefore(div1, statusEl);
}
function votingSave() {
if (!saving) {
var div = el('div');
div.id = 'voting-saving';
div.innerHTML = '<div id="voting-progress"><div id="voting-progress-progress" style="width:0%"> </div></div>' + data.strings.saveprog;
statusEl.parentNode.appendChild(div);
btnEl.setDisabled(true);
document.getElementById('voting-standard').style.visibility = 'hidden';
saving = {'cursor': 0, 'pages': []};
for (var i in votes) {
if (votes[i].value != 0 && votes[i].orig != votes[i].value) {
saving.pages[saving.cursor++] = {
'text': '\n# [[user:' + wgUserName + '|' + wgUserName + ']] ~' + '~~' + '~~\n',
'page': data.config.votepath + (votes[i].value==1?'+':'-') + '/' + i
};
}
}
saving.cursor = -5;
if (saving.pages.length) {
votingSave();
} else {
containerEl.innerHTML = data.strings.thankyou;
}
} else {
if (saving.cursor === -5) {
saving.cursor = 0;
}
if (saving.cursor === -1) {
return;
}
document.getElementById('voting-progress-progress').style.width = 100*(saving.cursor+1)/saving.pages.length + '%';
api.postWithToken('edit', {
'action': 'edit',
'notminor': 1,
'unwatch': 1,
'summary': data.strings.summary,
'title': saving.pages[saving.cursor].page,
'appendtext': saving.pages[saving.cursor].text
}).done(votingSave);
saving.cursor++;
if (!saving.pages[saving.cursor]) {
saving.cursor = -1;
containerEl.innerHTML = data.strings.thankyou;
}
}
}
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs', 'oojs-ui']).done(function () {
api = new mw.Api();
// Prepare dates
data.config.start = new Date(data.config.start);
if ( typeof data.config.votelength !== 'undefined' ) {
data.config.end = votingLength( data.config.start, data.config.votelength );
} else {
data.config.end = new Date( data.config.end );
}
// Prepare pages paths
data.config.pagepath = data.config.pagepath.replace('%config.path%', data.config.path);
data.config.talkpagepath = mw.util.getUrl( data.config.talkpagepath.replace('%config.path%', data.config.path) );
data.config.talkpath = data.config.talkpath.replace('%config.path%', data.config.path);
data.config.votepath = data.config.votepath.replace('%config.path%', data.config.path);
data.config.voteexceptionspath = data.config.voteexceptionspath.replace('%config.path%', data.config.path);
// Prepare strings
data.strings.justbeforesave = data.strings.justbeforesave.replace('%config.talkpagepath%', data.config.talkpagepath);
// Init
if ( containerEl ) {
containerEl.innerHTML = '';
statusEl = el('div');
statusEl.id = 'voting-status';
containerEl.appendChild(statusEl);
btnEl = new OO.ui.ButtonWidget( {
label: data.strings.votebutton,
flags: ['primary', 'progressive'],
id: 'voting-button'
} );
btnEl.on('click', votingStart);
containerEl.appendChild(btnEl.$element.get(0));
}
} );
})();