MediaWiki:Gadget-ondemand-arbcomVoting.js: различия между версиями
Перейти к навигации
Перейти к поиску
Содержимое удалено Содержимое добавлено
Kalan (обсуждение | вклад) ещё актуализация |
Putnik (обсуждение | вклад) поддержка тёмной темы |
||
(не показано 139 промежуточных версий 11 участников) | |||
Строка 1: | Строка 1: | ||
/* |
|||
(function(){ |
|||
* Скрипт для выборов в Арбитражный комитет (ВП:АК) |
|||
appendCSS('.cand-list { width: 25em } #voting-container ul { margin: 1.5em 0 } #voting-container ul li { width: 19em !important } #voting-help-final { margin: 3em 0 0; border-top: 1px solid #888} .cand-selected { background: #97B6E2 !important } #voting-limited-count { float: right; font-size: 300%; text-align: right; width: 1.5em; line-height: 100%; color: #ff5555 } #voting-limited-count.ok { color: #71C837; font-weight: bold }') |
|||
* |
|||
* Авторы: Kalan, Serhio Magpie, stjn |
|||
function en(e) { return encodeURIComponent(e) } |
|||
*/ |
|||
function el(e) { return document.createElement(e) } |
|||
function tstamp(t) { return !t.getUTCFullYear() ? null : // Safari + Chrome |
|||
$(function () { |
|||
(t.getUTCFullYear()+":0"+(t.getUTCMonth()+1)+":0"+t.getUTCDate()+":0"+ |
|||
var containerEl = document.getElementById( 'voting-container' ); |
|||
t.getUTCHours() +":0"+ t.getUTCMinutes() +":0"+t.getUTCSeconds()) |
|||
if ( containerEl === null ) { |
|||
.replace(/:0?(\d\d)/g, '$1') } |
|||
mw.log.warn( 'arbcomVoting: Aborted because no voting container was detected.' ); |
|||
function ch(o) { for (var i in o) { return o[i] } } |
|||
return; |
|||
} |
|||
var conf = { |
|||
'criteria': { |
|||
var wgUserName = mw.config.get( 'wgUserName' ); |
|||
'count': 500, |
|||
if ( wgUserName === null ) { |
|||
'registration': '2013-02-05T23:59:60Z' |
|||
mw.log.warn( 'arbcomVoting: Not logged in.' ); |
|||
}, |
|||
return; |
|||
'pagepath': 'Википедия:Выборы арбитров/Весна 2013/Голосование', |
|||
} |
|||
'talkpath': 'Википедия:Выборы арбитров/Весна 2013/Обсуждение/', |
|||
'votepath': 'Википедия:Выборы арбитров/Весна 2013/Голосование/', |
|||
/******* COMMON ******* */ |
|||
'start': new Date('May 23 2013 00:00 +0000'), |
|||
function tstamp(t) { |
|||
'end': new Date('May 30 2013 00:00 +0000') |
|||
return !t.getUTCFullYear() ? null : // Safari + Chrome |
|||
} |
|||
(t.getUTCFullYear()+":0"+(t.getUTCMonth()+1)+":0"+t.getUTCDate()+":0"+ |
|||
var ico = { |
|||
t.getUTCHours() +":0"+ t.getUTCMinutes() +":0"+t.getUTCSeconds()) |
|||
'up': '/upwiki/wikipedia/commons/thumb/', |
|||
.replace(/:0?(\d\d)/g, '$1'); |
|||
'supp' : '2/2d/Support-gray.svg/39px-Support-gray.svg.png', |
|||
} |
|||
'suppinact' : '8/8d/Support-colored.svg/39px-Support-colored.svg.png', |
|||
function ch(o) { for (var i in o) { return o[i] } } |
|||
'suppact' : '5/5b/Support-filled.svg/39px-Support-filled.svg.png', |
|||
'opp' : 'e/e7/Oppose-gray.svg/39px-Oppose-gray.svg.png', |
|||
/******* VARIABLES *******/ |
|||
'oppinact' : '0/06/Oppose-colored.svg/39px-Oppose-colored.svg.png', |
|||
'oppact' : '7/7d/Oppose-filled.svg/39px-Oppose-filled.svg.png' |
|||
var data = require( './ondemand-arbcomVoting.json' ); |
|||
} |
|||
var wgNamespaceNumber = mw.config.get( 'wgNamespaceNumber' ); |
|||
var loc = { |
|||
var votes = {}; |
|||
'votebutton': 'Проголосовать', |
|||
var saving = null; |
|||
'statuscriteria': 'Проверка критериев…', |
|||
var api; |
|||
'criteriafail': '<h3 class="voting-error">Вы не соответствуете критериям.</h3><p>Ваши голоса на этих выборах <b>не будут засчитаны</b>.</p><p>Впрочем, если вам просто любопытно, как работает скрипт, вы можете посмотреть на него без сохранения голосов.</p>', |
|||
var statusEl; |
|||
'criteriafailbutton': 'Посмотреть', |
|||
var btnEl; |
|||
'loadingvotes': 'Проверка имеющихся голосов…', |
|||
'draghere': 'Перетащите первого кандидата сюда', |
|||
/******* MAIN *******/ |
|||
'votinghelp': '<div class="voting-help"><h4>Основная часть</h4>\ |
|||
function votingLength( date, days ) { |
|||
<p>Расставьте голоса «за» и «против» рядом с именами тех кандидатов, относительно которых у вас сформировано мнение. Вы сможете дополнить или изменить выбор позже.</p>\ |
|||
var result = new Date( date ); |
|||
</div>', |
|||
result.setDate( result.getDate() + days ); |
|||
'etchelp1': '<div class="voting-help"><h4>Дополнительная часть</h4>\ |
|||
return result; |
|||
<p>Дополнительная часть состоит из нескольких опросников. Вы можете принять участие не во всех или вовсе проигнорировать этот блок, но лучше всего заполните все три. Это поможет улучшить процедуру выборов.</p></div>\ |
|||
} |
|||
<div class="voting-help"><h5>Метод Шульце</h5>\ |
|||
<ol>\ |
|||
function votingStart() { |
|||
<li>Перетащите какого-нибудь подходящего, на ваш взгляд, кандидата на первый уровень.</li>\ |
|||
$( '.voting-container' ).addClass( 'active' ); |
|||
<li>Перетаскивайте остальных кандидатов в этом же направлении. Кандидатов можно оставлять на существующих уровнях (отмечены цифрами) или на новых (места для их создания обозначены пунктиром). Во время перетаскивания пунктир показывает относительное расположение кандидата в списке.</li>\ |
|||
statusEl.textContent = data.strings.statuscriteria; |
|||
<li>Перед сохранением убедитесь в соответствии расстановки вашему мнению: более хорошие кандидаты выше, менее хорошие ниже, равные на одном уровне, совсем неподходящие в разделе «нет предпочтения». Если вы являетесь одним из кандидатов, <em>не оставляйте</em> себя в нижней секции.</li>\ |
|||
btnEl.setDisabled(true); |
|||
</ol>\ |
|||
btnEl.$element.css( 'display', 'none' ); |
|||
</div>', |
|||
'etchelp2': '<div id="voting-etc-schulze"></div>\ |
|||
api.get({ |
|||
<div class="voting-help"><h5>Целевой набор резервных арбитров</h5>\ |
|||
'action': 'query', |
|||
<p>Оцените, насколько каждый кандидат пригоден на роль <strong>именно резервного арбитра</strong>. Если, по вашему мнению, кандидат будет хорош только как основной арбитр, <strong>не голосуйте за</strong>.</p>\ |
|||
'list': 'users', |
|||
</div>', |
|||
'usprop': ['registration', 'editcount'], |
|||
'etchelp3': '<div class="voting-help"><h5>Идеальный состав</h5>\ |
|||
'prop': 'revisions', |
|||
<p>Выберите <strong>ровно пять</strong> арбитров из списка. Голос за большее или меньшее количество арбитров будет сохранён, но проигнорирован при подсчёте.</p>\ |
|||
'rvprop': 'content', |
|||
</div>', |
|||
'ususers': wgUserName, |
|||
'etchelp4': '<div class="voting-help" id="voting-help-final"><p>До конца выборов вы сможете изменить своё мнение.</p>\ |
|||
'titles': data.config.voteexceptionspath |
|||
<p>Если что-то осталось непонятным, можно <a target="_blank" href="/ruwiki/wiki/Обсуждение_Википедии:Выборы_арбитров/Весна_2013/Голосование">задать вопрос</a>.</p></div>', |
|||
}).done(votingStartContinue); |
|||
'nopreference': 'нет предпочтения', |
|||
} |
|||
'newlevel': 'новый уровень', |
|||
'justbeforesave': 'Смело направляйте любые отзывы, предложения и сообщения об ошибках на <a href="/ruwiki/wiki/MediaWiki_talk:Script/Voting.js">страницу обсуждения</a>.', |
|||
function votingStartContinue(d) { |
|||
'savebutton': 'Сохранить', |
|||
query = d.query; |
|||
'saveprog': '<b>Не закрывайте страницу</b> до завершения сохранения.', |
|||
pages = query.pages; |
|||
'summary': '\u200Bг\u200Bол\u200Bос\u200B', |
|||
userinfo = query.users[0]; |
|||
'thankyou': '<h3>Спасибо за участие в выборах!</h3><p>Вы сможете изменить ваши голоса до конца голосования.</p><p>Только что отданные голоса можно посмотреть на <a href="/ruwiki/wiki/Special:Mycontributions">странице вклада</a>.</p>' |
|||
if ((// a valid voter must be a non-anonymous user |
|||
} |
|||
typeof userinfo === 'undefined' || typeof userinfo.missing !== 'undefined' || |
|||
var cand = 'Be nt all!Deinocheirus!Malbakov Korkem Shamshievih!Melirius!Neolexx!Rubin16!Scorpion-811!Vajrapani!Vlsergey!Джекалоп!Дядя Фред!Цезарь Валентиниан'.split('!') |
|||
// who is not blocked |
|||
var wgAPIPath = wgServer + wgScriptPath + '/api.php?format=json&' |
|||
userinfo.blockedby || |
|||
// whose editcount is at least data.config.criteria.count |
|||
var votes = {} |
|||
userinfo.editcount < data.config.criteria.count || |
|||
var criteriaMatch |
|||
// and who is registered no later than data.config.criteria.registration |
|||
var saving = null |
|||
// "null" means "before 2005-12-29", user creation was not logged before then |
|||
var aj = sajax_init_object() |
|||
(userinfo.registration !== null && userinfo.registration > data.config.criteria.registration)) && |
|||
var aj2 = sajax_init_object() |
|||
// exemptions |
|||
var token |
|||
('\n' + ch(pages).revisions[0]['*'] + '\n').indexOf('\n* %\n'.replace('%', wgUserName)) == -1 |
|||
) { |
|||
// drag-n-drop |
|||
votingCriteriaFail(); |
|||
var coo |
|||
} else { |
|||
var cooD |
|||
votingContinue(); |
|||
var drag, dragGhost, dragItem, dragOriginal |
|||
} |
|||
var dragHere |
|||
} |
|||
function votingStart() { |
|||
function votingCriteriaFail() { |
|||
importStylesheetURI('//ru.wikipedia.org/ruwiki/w/index.php?title=MediaWiki:Voting7.css&action=raw&ctype=text/css') |
|||
statusEl.textContent = ''; |
|||
btn.disabled = true |
|||
$( '#voting-msg-criteriafail' ).show(); |
|||
btn.style.display = 'none' |
|||
} |
|||
status.innerHTML = loc.statuscriteria |
|||
aj2.onreadystatechange = votingStartContinue |
|||
function votingContinue() { |
|||
aj2.open('GET', wgAPIPath + 'action=query&list=users&usprop=registration|editcount&prop=revisions&rvprop=content&ususers=' + |
|||
statusEl.textContent = data.strings.loadingvotes; |
|||
en(wgUserName) + '&titles=' + en(conf.votepath + '^'), true) |
|||
// latest contributions in ns:4 |
|||
aj2.send('') |
|||
api.get({ |
|||
} |
|||
'action': 'query', |
|||
function votingStartContinue() { |
|||
'list': 'usercontribs', |
|||
if (aj2.readyState != 4) return |
|||
'ucnamespace': wgNamespaceNumber, |
|||
if (aj2.status != 200) { // temporary problems? |
|||
'uclimit': 500, |
|||
votingStart() |
|||
'ucuser': wgUserName, |
|||
return |
|||
'ucend': tstamp( data.config.start ), |
|||
} |
|||
'ucdir': 'older' |
|||
}).done(votingDraw); |
|||
query = eval('(' + aj2.responseText + ')').query |
|||
} |
|||
userinfo = query.users[0] |
|||
if ((// a valid voter must be a non-anonymous user |
|||
function onClick( e ) { |
|||
userinfo.missing !== undefined || |
|||
e.preventDefault(); |
|||
// who is not blocked |
|||
var button = e.currentTarget; |
|||
userinfo.blockedby || |
|||
var span = button.querySelector( 'span' ); |
|||
// whose editcount is at least conf.criteria.count |
|||
var tr = button.parentNode.parentNode; |
|||
userinfo.editcount < conf.criteria.count || |
|||
var candidate = button.dataset.candidate; |
|||
// and who is registered no later than conf.criteria.registration |
|||
var type = button.dataset.type; |
|||
// "null" means "before 2005-12-29", user creation was not logged before then |
|||
var active = Boolean( button.getAttribute( 'aria-checked' ) ); |
|||
(userinfo.registration !== null && userinfo.registration > conf.criteria.registration)) && |
|||
var option = data.options[ type ]; |
|||
// exemptions |
|||
('\n' + ch(query.pages).revisions[0]['*'] + '\n').indexOf('\n* %\n'.replace('%', wgUserName)) == -1) |
|||
// Update other button first |
|||
votingCriteriaFail() |
|||
var otherButton = tr.querySelector( `.vote-button[aria-checked="true"]` ); |
|||
else { |
|||
var otherType = otherButton && otherButton.dataset.type; |
|||
criteriaMatch = 1 |
|||
if ( otherButton ) { |
|||
votingContinue() |
|||
var otherVote = otherType === 'support' ? 1 : -1; |
|||
} |
|||
otherButton.setAttribute( 'aria-checked', false ); |
|||
} |
|||
} |
|||
function votingCriteriaFail() { |
|||
status.innerHTML = loc.criteriafail |
|||
// Change vote |
|||
btn.style.display = '' |
|||
var vote = type === 'support' ? 1 : -1; |
|||
btn.disabled = false |
|||
var sameVote = votes[ candidate ].value === vote; |
|||
btn.value = loc.criteriafailbutton |
|||
votes[ candidate ].value = ( sameVote ? 0 : vote ); |
|||
btn.onclick = function(){criteriaMatch = 0; votingContinue()} |
|||
button.setAttribute( 'aria-checked', !sameVote ); |
|||
} |
|||
function votingContinue() { |
|||
// Update table row |
|||
status.innerHTML = loc.loadingvotes |
|||
tr.className = !sameVote ? 'vote-' + type : ''; |
|||
btn.style.display = '' |
|||
} |
|||
btn.onclick = votingToken |
|||
btn.value = loc.savebutton |
|||
function addVoteButton( candidate, type, previousVote ) { |
|||
btn.disabled = true |
|||
var option = data.options[ type ]; |
|||
// latest contributions in ns:4 + Schulze method page |
|||
var didVote = previousVote === ( type === 'support' ? 1 : -1 ); |
|||
aj.open('GET', wgAPIPath + 'action=query' + |
|||
var votedOpposite = previousVote === ( type === 'support' ? -1 : 1 ); |
|||
'&list=usercontribs&ucnamespace=4&uclimit=500' + |
|||
var voteType = votedOpposite ? 'changevote' : 'novote'; |
|||
'&ucuser=' + en(wgUserName) + |
|||
(tstamp(conf.start) ? '&ucend=' + tstamp(conf.start) : '') + |
|||
var button = document.createElement( 'button' ); |
|||
'&ucdir=older' + |
|||
button.className = 'vote-button skin-invert'; |
|||
'&fake=1&prop=revisions&rvprop=content' + |
|||
button.setAttribute( 'role', 'switch' ); |
|||
'&titles=' + en(conf.votepath+'$|') |
|||
button.setAttribute( 'type', 'button' ); |
|||
+ en(conf.votepath+'%|') |
|||
button.setAttribute( 'data-candidate', candidate ); |
|||
+ en(conf.votepath+'=') , |
|||
button.setAttribute( 'data-type', type ); |
|||
true) |
|||
if ( didVote ) { |
|||
aj.onreadystatechange = votingDraw |
|||
button.setAttribute( 'data-voted', true ); |
|||
aj.send('') |
|||
} |
|||
} |
|||
button.setAttribute( 'aria-checked', didVote ); |
|||
function votingDraw() { |
|||
button.addEventListener( 'click', onClick ); |
|||
if (aj.readyState != 4) return |
|||
if (aj.status != 200) { // temporary problems? |
|||
var span = document.createElement( 'span' ); |
|||
votingContinue() |
|||
span.textContent = option[ voteType ]; |
|||
return |
|||
if ( votedOpposite ) { |
|||
} |
|||
span.textContent = span.textContent.replace( '%candidate%', candidate ); |
|||
btn.disabled = !criteriaMatch |
|||
} |
|||
status.innerHTML = '' |
|||
button.title = span.textContent; |
|||
var query = eval('(' + aj.responseText + ')') |
|||
button.appendChild( span ); |
|||
query = query.query |
|||
return button; |
|||
var co = query.usercontribs |
|||
} |
|||
for (var k in query.pages) { |
|||
spl = query.pages[k].revisions[0]['*'].split('\n') |
|||
function votingDraw(d) { |
|||
switch (query.pages[k].title.match(/(.)$/)[1]) { |
|||
statusEl.textContent = ''; |
|||
case '$': |
|||
btnEl.setLabel(data.strings.savebutton); |
|||
var sc = spl |
|||
btnEl.setDisabled(false); |
|||
break |
|||
btnEl.off('click').on('click', votingSave); |
|||
case '%': |
|||
btnEl.$element.css( 'display', '' ); |
|||
var reserve = spl |
|||
break |
|||
var co = ( d.query && d.query.usercontribs ) || []; |
|||
case '=': |
|||
var limited = spl |
|||
// retrieving votes, according to contributions, only latest ones are valid |
|||
break |
|||
for ( var i = 0; i < data.candidates.length; i++ ) { |
|||
} |
|||
votes[ data.candidates [ i ] ] = { 'orig': 0, 'value': 0 }; |
|||
} |
|||
} |
|||
for ( var i = co.length - 1; i >= 0; i-- ) { |
|||
// retrieving "traditional" votes, according to contributions, only latest ones are valid |
|||
var match = false; |
|||
for (i=0; i<cand.length; i++) votes[cand[i]] = {'orig':0, 'value':0} |
|||
if ( co[ i ].title.startsWith( data.config.votepath ) ) { |
|||
for (var i=co.length-1; i>=0; i--) { |
|||
match = co[ i ].title.match( /\/([+-])\/(.*?)$/ ); |
|||
} |
|||
if (m) m = co[i].title.match(/\/([+-])\/(.*?)$/) |
|||
if ( match ) { |
|||
if (m) votes[m[2]] = { 'orig' : m[1]=='+' ? +1 : -1, |
|||
var hasSummary = co[ i ].comment === data.strings.summary; |
|||
'value': 0 } |
|||
if ( hasSummary ) { |
|||
} |
|||
votes[ match[ 2 ] ] = { |
|||
'orig': match[ 1 ] === '+' ? +1 : -1, |
|||
// retrieving Schulze method page |
|||
'value': 0 |
|||
// we assume that the bot will immediately revert not-well-formed edits |
|||
}; |
|||
for (i=0; i<sc.length; i++) { |
|||
} |
|||
if (sc[i].indexOf('* ' + wgUserName + ' | ') == 0) { |
|||
} |
|||
votes._s = {'reading': true} |
|||
} |
|||
var j = 0 |
|||
continue |
|||
var div1 = document.createElement( 'div' ); |
|||
} |
|||
div1.id = 'voting-standard'; |
|||
if (!votes._s) continue |
|||
var help = document.createElement( 'p' ); |
|||
if (sc[i].indexOf('* ') == 0) votes._s.reading = undefined |
|||
help.textContent = data.strings.votinghelp; |
|||
if (votes._s.reading) { |
|||
div1.appendChild( help ); |
|||
j++ |
|||
var tab = document.createElement( 'table' ); |
|||
var m = sc[i].match(/^\*(.) (.*)/) |
|||
tab.className = 'voting-table'; |
|||
m[2] = m[2].split(' | ') |
|||
votes._s[m[1]=='#'?j:0] = m[2] |
|||
var caption = document.createElement( 'caption' ); |
|||
} |
|||
caption.textContent = data.strings.votebutton; |
|||
} |
|||
tab.appendChild( caption ); |
|||
if (!votes._s) { |
|||
votes._s = {0: cand} |
|||
for ( var candidate in votes ) { |
|||
} |
|||
if ( candidate.match( /^_[srl]$/ ) ) { |
|||
if (!votes._s[1]) votes._s[1] = [] |
|||
continue; |
|||
if (!votes._s[0]) votes._s[0] = [] |
|||
} |
|||
if (votes._s.reading) votes._s.reading = undefined |
|||
var tr = document.createElement( 'tr' ); |
|||
var th = document.createElement( 'th' ); |
|||
// retrieving "reserve" voting |
|||
th.setAttribute( 'scope', 'row' ); |
|||
votes._r = {} |
|||
var lin = document.createElement('a'); |
|||
for (i = 0; i < cand.length; ++i) |
|||
lin.href = mw.util.getUrl( data.config.talkpath + candidate ); |
|||
votes._r[cand[i]] = { 'orig' : 0, 'value': 0 } |
|||
lin.target = '_blank'; |
|||
reading = false |
|||
lin.textContent = candidate; |
|||
for (i=0; i<reserve.length; i++) { |
|||
lin.title = data.strings.questionsTooltip.replace( '%candidate%', candidate ); |
|||
if (reserve[i].indexOf('* ' + wgUserName + ' | ') == 0) { |
|||
th.appendChild( lin ); |
|||
reading = true |
|||
tr.appendChild( th ); |
|||
continue |
|||
} |
|||
// Support button |
|||
if (!votes._r) continue |
|||
var td1 = document.createElement( 'td' ); |
|||
if (reserve[i].indexOf('* ') == 0) reading = false |
|||
td1.appendChild( addVoteButton( candidate, 'support', votes[ candidate ].orig ) ); |
|||
if (reading) { |
|||
tr.appendChild( td1 ); |
|||
m = reserve[i].match(/^\*: (.) (.*)$/) |
|||
votes._r[m[2]].orig = votes._r[m[2]].value = ((m[1] == '.') ? 0 : (m[1] == '+' ? 1 : -1)) |
|||
// Oppose button |
|||
} |
|||
var td2 = document.createElement( 'td' ); |
|||
} |
|||
td2.appendChild( addVoteButton( candidate, 'oppose', votes[ candidate ].orig ) ); |
|||
tr.appendChild( td2 ); |
|||
// retrieving "limited" voting |
|||
tab.appendChild( tr ); |
|||
votes._l = {} |
|||
} |
|||
for (i = 0; i < cand.length; ++i) |
|||
votes._l[cand[i]] = { 'orig' : 0, 'value': 0 } |
|||
div1.appendChild(tab); |
|||
reading = false |
|||
for (i=0; i<limited.length; i++) { |
|||
statusEl.textContent = ''; |
|||
if (limited[i].indexOf('* ' + wgUserName + ' | ') == 0) { |
|||
$( '#voting-msg-justbeforesave' ).show(); |
|||
reading = true |
|||
statusEl.parentNode.insertBefore(div1, statusEl); |
|||
continue |
|||
} |
|||
} |
|||
if (!votes._l) continue |
|||
function showThankYou() { |
|||
if (limited[i].indexOf('* ') == 0) reading = false |
|||
containerEl.innerHTML = ''; |
|||
if (reading) { |
|||
$( '.voting-msg' ).hide(); |
|||
m = limited[i].match(/^\*: (.) (.*)$/) |
|||
$( '#voting-msg-thankyou' ).show(); |
|||
votes._l[m[2]].orig = votes._l[m[2]].value = ((m[1] == '.') ? 0 : 1) |
|||
} |
|||
} |
|||
} |
|||
function votingSave() { |
|||
if (!saving) { |
|||
var div = document.createElement('div'); |
|||
// drawing |
|||
div.className = 'voting-saving'; |
|||
var div1 = el('div') |
|||
div.textContent = data.strings.saveprog; |
|||
div1.id = 'voting-standard' |
|||
div.insertAdjacentHTML( 'afterbegin', '<div class="voting-progress"><div class="voting-progress-progress" style="width:0%"> </div></div>' ); |
|||
div1.innerHTML = loc.votinghelp |
|||
statusEl.parentNode.appendChild( div ); |
|||
var tab = el('table') |
|||
btnEl.setDisabled( true ); |
|||
$( '#voting-standard' ).hide(); |
|||
var tr, img, td1, td2, td3, lin |
|||
for (i in votes) { |
|||
saving = { 'cursor': 0, 'pages': [] }; |
|||
if (!i.match(/^_[srl]$/)) { |
|||
tr = el('tr') |
|||
for (var i in votes) { |
|||
td1 = el('td') |
|||
if (votes[i].value != 0 && votes[i].orig != votes[i].value) { |
|||
img = el('img') |
|||
saving.pages[saving.cursor++] = { |
|||
img.alt = '+' |
|||
'text': '\n# [[user:' + wgUserName + '|' + wgUserName + ']] ~' + '~~' + '~~\n', |
|||
img.width = img.height = 39 |
|||
'page': data.config.votepath + (votes[i].value==1?'+':'-') + '/' + i |
|||
}; |
|||
lin = el('a') |
|||
} |
|||
lin.href = '#' |
|||
} |
|||
lin.title = '+' |
|||
lin.appendChild(img) |
|||
saving.cursor = -5; |
|||
td1.appendChild(lin) |
|||
if (saving.pages.length) { |
|||
votingSave(); |
|||
td2 = el('td') |
|||
} else { |
|||
img = el('img') |
|||
showThankYou(); |
|||
img.alt = '−' |
|||
} |
|||
img.width = img.height = 39 |
|||
} else { |
|||
img.src = ico.up + (votes[i].orig == -1 ? ico.oppinact : ico.opp) |
|||
if (saving.cursor === -5) { |
|||
lin = el('a') |
|||
saving.cursor = 0; |
|||
lin.href = '#' |
|||
} |
|||
lin.title = '-' |
|||
if (saving.cursor === -1) { |
|||
lin.appendChild(img) |
|||
return; |
|||
td2.appendChild(lin) |
|||
} |
|||
td3 = el('td') |
|||
document.querySelector( '.voting-progress-progress' ).style.width = 100*(saving.cursor+1)/saving.pages.length + '%'; |
|||
td3.className = 'talklink' |
|||
lin = el('a') |
|||
api.postWithToken('edit', { |
|||
lin.href = wgServer + wgScript + '?title=' + encodeURI(conf.talkpath + i) |
|||
'action': 'edit', |
|||
lin.target = '_blank' |
|||
'notminor': 1, |
|||
lin.innerHTML = i |
|||
'unwatch': 1, |
|||
'assert': 'user', |
|||
td3.appendChild(lin) |
|||
'summary': data.strings.summary, |
|||
'title': saving.pages[saving.cursor].page, |
|||
tr.appendChild(td1) |
|||
'appendtext': saving.pages[saving.cursor].text |
|||
tr.appendChild(td2) |
|||
}).done(votingSave); |
|||
tr.appendChild(td3) |
|||
saving.cursor++; |
|||
tab.appendChild(tr) |
|||
if (!saving.pages[saving.cursor]) { |
|||
} |
|||
saving.cursor = -1; |
|||
} |
|||
showThankYou(); |
|||
div1.appendChild(tab) |
|||
} |
|||
// IE has problems inserting the table |
|||
} |
|||
div1.innerHTML += '' |
|||
} |
|||
var imgs = div1.getElementsByTagName('img') |
|||
for (i=0; i<imgs.length; i++) |
|||
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs', 'oojs-ui']).done(function () { |
|||
imgs[i].parentNode.onclick = oncl |
|||
api = new mw.Api(); |
|||
dragHere = el('span') |
|||
// Prepare dates |
|||
dragHere.id = 'voting-draghere' |
|||
data.config.start = new Date(data.config.start); |
|||
dragHere.innerHTML = loc.draghere |
|||
if ( typeof data.config.votelength !== 'undefined' ) { |
|||
data.config.end = votingLength( data.config.start, data.config.votelength ); |
|||
var divetc = el('div') |
|||
} else { |
|||
divetc.id = 'voting-schulze' |
|||
data.config.end = new Date( data.config.end ); |
|||
} |
|||
var div2 = el('div') |
|||
// Prepare pages paths |
|||
div2.id = 'voting-etc-schulze' |
|||
data.config.pagepath = data.config.pagepath.replace('%config.path%', data.config.path); |
|||
div2.innerHTML = loc.etchelp1 |
|||
data.config.talkpagepath = mw.util.getUrl( data.config.talkpagepath.replace('%config.path%', data.config.path) ); |
|||
var ul = el('ul') |
|||
data.config.talkpath = data.config.talkpath.replace('%config.path%', data.config.path); |
|||
ul.id = 'schulze-list' |
|||
data.config.votepath = data.config.votepath.replace('%config.path%', data.config.path); |
|||
var gw, theid, can, first, level, spc |
|||
data.config.voteexceptionspath = data.config.voteexceptionspath.replace('%config.path%', data.config.path); |
|||
var c |
|||
// Init |
|||
for (i=1; i!=null; i==0 ? i=null : i++) { |
|||
if ( containerEl ) { |
|||
gw = el('li') |
|||
containerEl.innerHTML = ''; |
|||
gw.className = 'groundwork' |
|||
theid = el('div') |
|||
statusEl = document.createElement('div'); |
|||
theid.className = 'li-id' |
|||
statusEl.className = 'voting-status voting-msg'; |
|||
theid.innerHTML = !votes._s[i] ? i : (i==1 ? '' : i-1) + '…' + i |
|||
containerEl.appendChild( statusEl ); |
|||
$( '.voting-msg a' ).attr( 'target', '_blank' ); |
|||
if (!votes._s[i]) i=0 |
|||
btnEl = new OO.ui.ButtonWidget( { |
|||
level = el('li') |
|||
label: data.strings.votebutton, |
|||
level.className = 'level' |
|||
flags: ['primary', 'progressive'], |
|||
if (i==0) level.id = 'voting-no-preference' |
|||
id: 'voting-button' |
|||
if (i==1 && votes._s[i].join('!') == '') { |
|||
} ); |
|||
level.appendChild(dragHere) |
|||
btnEl.on('click', votingStart); |
|||
} |
|||
containerEl.appendChild(btnEl.$element.get(0)); |
|||
} |
|||
theid = el('div') |
|||
} ); |
|||
theid.className = 'li-id' |
|||
}); |
|||
theid.innerHTML = i==0 ? loc.nopreference : i |
|||
level.appendChild(theid) |
|||
for (c=0; c<votes._s[i].length; c++) { |
|||
can = el('a') |
|||
can.onclick = function() { return false } |
|||
can.onmousedown = boxMouseDown |
|||
can.innerHTML = votes._s[i][c] |
|||
can.className = 'cand' |
|||
level.appendChild(can) |
|||
} |
|||
level.appendChild(spacer()) |
|||
ul.appendChild(gw) |
|||
ul.appendChild(level) |
|||
} |
|||
div2.appendChild(ul) |
|||
divetc.appendChild(div2) |
|||
var div2 = el('div') |
|||
div2.id = 'voting-etc-reserve' |
|||
div2.innerHTML = loc.etchelp2 |
|||
var tab = el('table') |
|||
var tr, img, td1, td2, td3, lin |
|||
for (i in votes._r) { |
|||
tr = el('tr') |
|||
td1 = el('td') |
|||
img = el('img') |
|||
img.alt = '+' |
|||
img.width = img.height = 19 |
|||
img.src = ico.up + (votes._r[i].orig == 1 ? ico.suppinact : ico.supp) |
|||
lin = el('a') |
|||
lin.href = '#' |
|||
lin.title = '+' |
|||
lin.appendChild(img) |
|||
td1.appendChild(lin) |
|||
td2 = el('td') |
|||
img = el('img') |
|||
img.alt = '−' |
|||
img.width = img.height = 19 |
|||
img.src = ico.up + (votes._r[i].orig == -1 ? ico.oppinact : ico.opp) |
|||
lin = el('a') |
|||
lin.href = '#' |
|||
lin.title = '-' |
|||
lin.appendChild(img) |
|||
td2.appendChild(lin) |
|||
td3 = el('td') |
|||
td3.className = 'talklink' |
|||
lin = el('a') |
|||
lin.href = wgServer + wgScript + '?title=' + encodeURI(conf.talkpath + i) |
|||
lin.target = '_blank' |
|||
lin.innerHTML = i |
|||
td3.appendChild(lin) |
|||
tr.appendChild(td1) |
|||
tr.appendChild(td2) |
|||
tr.appendChild(td3) |
|||
tab.appendChild(tr) |
|||
} |
|||
div2.appendChild(tab) |
|||
var imgs = div2.getElementsByTagName('img') |
|||
for (i=0; i<imgs.length; i++) { |
|||
imgs[i].src = imgs[i].src.replace(/39px/, '19px') |
|||
imgs[i].parentNode.onclick = onclr |
|||
} |
|||
divetc.appendChild(div2) |
|||
var div2 = el('div') |
|||
div2.id = 'voting-etc-limited' |
|||
div2.innerHTML = loc.etchelp3 |
|||
var count = 0 |
|||
var hol = el('div') |
|||
hol.className = 'cand-list' |
|||
for (i in votes._l) { |
|||
one = el('a') |
|||
one.innerHTML = i |
|||
one.className = votes._l[i].orig ? 'cand cand-selected' : 'cand' |
|||
count += votes._l[i].orig |
|||
one.href = '#' |
|||
one.onclick = oncll |
|||
hol.appendChild(one) |
|||
} |
|||
var cnt = el('div') |
|||
cnt.id = 'voting-limited-count' |
|||
cnt.innerHTML = count |
|||
if (count == 5) cnt.className = 'ok' |
|||
div2.appendChild(cnt) |
|||
div2.appendChild(hol) |
|||
divetc.appendChild(div2) |
|||
var div2 = el('div') |
|||
div2.innerHTML = loc.etchelp4 |
|||
divetc.appendChild(div2) |
|||
if (navigator.appName=='Microsoft Internet Explorer') { |
|||
// troubles with scopes |
|||
document.onmousemove = mouseMove |
|||
document.onmouseup = mouseUp |
|||
} else { |
|||
hookEvent('mousemove', mouseMove) |
|||
hookEvent('mouseup', mouseUp) |
|||
} |
|||
status.innerHTML = loc.justbeforesave |
|||
status.parentNode.insertBefore(div1, status) |
|||
status.parentNode.insertBefore(divetc, status) |
|||
} |
|||
function votingToken() { |
|||
aj = sajax_init_object() |
|||
aj.onreadystatechange = votingTokenContinue |
|||
aj.open('GET', wgAPIPath + 'action=query&prop=info&intoken=edit&titles=42', true) |
|||
aj.send('') |
|||
} |
|||
function votingTokenContinue() { |
|||
if (aj.readyState != 4) return |
|||
if (aj.readyState == 4 && aj.status != 200) { |
|||
votingToken() |
|||
return |
|||
} |
|||
token = eval('(' + aj.responseText + ')') |
|||
token = ch(token.query.pages).edittoken |
|||
votingSave() |
|||
} |
|||
function votingSave() { |
|||
if (saving == null) { |
|||
var div = el('div') |
|||
div.id = 'voting-saving' |
|||
div.innerHTML = '<div id="voting-progress"><div id="voting-progress-progress" style="width:0%"> </div></div>' + loc.saveprog |
|||
status.parentNode.appendChild(div) |
|||
btn.disabled = true |
|||
document.getElementById('voting-schulze' ).style.visibility = 'hidden' |
|||
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': conf.votepath + (votes[i].value==1?'+':'-') + '/' + i} |
|||
} |
|||
} |
|||
var text, text2, changed |
|||
var ms, mi |
|||
var li = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
text = text2 = '\n* ' + wgUserName + ' | ~' + '~~' + '~~\n' |
|||
for (i=0; i<li.length; i++) { |
|||
ms = li[i].getElementsByTagName('a') |
|||
if (ms.length) { |
|||
mi = [] |
|||
for (var j=0; j<ms.length; j++) mi[mi.length] = ms[j].innerHTML |
|||
text += (li[i].id == 'voting-no-preference' ? '*: ' : '*# ') + mi.join(' | ') + '\n' |
|||
} |
|||
} |
|||
for (i=1; i!=null; i==0 ? i=null : i++) { |
|||
if (!votes._s[i]) i=0 |
|||
if (votes._s[i].join('.')) text2 += (i==0 ? '*: ' : '*# ') + votes._s[i].join(' | ') + '\n' |
|||
} |
|||
if (text != text2) { |
|||
saving.pages[saving.cursor++] = {'page': conf.votepath + '$', |
|||
'text': text} |
|||
} |
|||
text = '\n* ' + wgUserName + ' | ~' + '~~' + '~~\n' |
|||
changed = false |
|||
for (var i in votes._l) { |
|||
text += '*: ' + (votes._l[i].value ? '+' : '.') + ' ' + i + '\n' |
|||
if (votes._l[i].orig != votes._l[i].value) |
|||
changed = true |
|||
} |
|||
if (changed) { |
|||
saving.pages[saving.cursor++] = {'page': conf.votepath + '=', |
|||
'text': text} |
|||
} |
|||
text = '\n* ' + wgUserName + ' | ~' + '~~' + '~~\n' |
|||
changed = false |
|||
var vd = {99: '-', 100: '.', 101: '+'} |
|||
for (var i in votes._r) { |
|||
text += '*: ' + vd[votes._r[i].value + 100] + ' ' + i + '\n' |
|||
if (votes._l[i].orig != votes._l[i].value) |
|||
changed = true |
|||
} |
|||
if (changed) { |
|||
saving.pages[saving.cursor++] = {'page': conf.votepath + '%', |
|||
'text': text} |
|||
} |
|||
saving.cursor = -5 |
|||
if (saving.pages.length) |
|||
votingSave() |
|||
else |
|||
document.getElementById('voting-container').innerHTML = loc.thankyou |
|||
} else { |
|||
if (saving.cursor != -5 && aj.readyState != 4) return |
|||
if (aj.readyState == 4 && aj.status != 200) { |
|||
votingSave() |
|||
return |
|||
} |
|||
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 + '%' |
|||
aj = sajax_init_object() |
|||
aj.open('POST', wgAPIPath + 'action=edit¬minor=1&unwatch=1&token=' + en(token) + '&summary=' + en(loc.summary) + |
|||
'&title=' + en(saving.pages[saving.cursor].page) + |
|||
'&appendtext=' + en(saving.pages[saving.cursor].text), true) |
|||
aj.onreadystatechange = votingSave |
|||
aj.send('') |
|||
saving.cursor++ |
|||
if (!saving.pages[saving.cursor]) { |
|||
saving.cursor = -1 |
|||
document.getElementById('voting-container').innerHTML = loc.thankyou |
|||
} |
|||
} |
|||
} |
|||
// onclick() for round buttons |
|||
function oncl() { |
|||
var imgs = this.parentNode.parentNode.getElementsByTagName('img') |
|||
var link = this.parentNode.parentNode.getElementsByTagName('a')[2] |
|||
var ca = link.innerHTML |
|||
var ti = this.title |
|||
var vo = ti=='+'?1:-1 |
|||
votes[ca].value = (votes[ca].value==vo) ? 0 : vo |
|||
imgs[0].src = ico.up + (votes[ca].value== 1 ?ico.suppact:(votes[ca].orig== 1 ?ico.suppinact:ico.supp)) |
|||
imgs[1].src = ico.up + (votes[ca].value==-1 ?ico.oppact :(votes[ca].orig==-1 ?ico.oppinact :ico.opp)) |
|||
link.className = ['opp', '', 'supp'][votes[ca].value + 1] |
|||
return false |
|||
} |
|||
function onclr() { |
|||
var imgs = this.parentNode.parentNode.getElementsByTagName('img') |
|||
var link = this.parentNode.parentNode.getElementsByTagName('a')[2] |
|||
var ca = link.innerHTML |
|||
var ti = this.title |
|||
var vo = ti=='+'?1:-1 |
|||
votes._r[ca].value = (votes._r[ca].value==vo) ? 0 : vo |
|||
imgs[0].src = ico.up + (votes._r[ca].value== 1 ?ico.suppact:(votes._r[ca].orig== 1 ?ico.suppinact:ico.supp)).replace(/39px/, '19px') |
|||
imgs[1].src = ico.up + (votes._r[ca].value==-1 ?ico.oppact :(votes._r[ca].orig==-1 ?ico.oppinact :ico.opp)).replace(/39px/, '19px') |
|||
link.className = ['opp', '', 'supp'][votes._r[ca].value + 1] |
|||
return false |
|||
} |
|||
function oncll() { |
|||
if (this.className == 'cand') |
|||
this.className = 'cand cand-selected' |
|||
else |
|||
this.className = 'cand' |
|||
var ca = this.innerHTML |
|||
votes._l[ca].value = (this.className == 'cand')?0:1 |
|||
var count = 0 |
|||
for (var i in votes._l) |
|||
count += votes._l[i].value |
|||
document.getElementById('voting-limited-count').innerHTML = count |
|||
document.getElementById('voting-limited-count').className = (count==5)?'ok':'' |
|||
return false |
|||
} |
|||
// used by drag-n-drop |
|||
function disableSelection(el, g){ |
|||
// doesn't work for Opera, thus onmousemove drops selection ranges |
|||
el.onselectstart = g ? function(){ return false } : null |
|||
el.unselectable = g ? "on" : "off" |
|||
el.style.MozUserSelect = g ? "none" : "" |
|||
} |
|||
function absolutePosition(el) { |
|||
q = el |
|||
co = {'x': 0, 'y': 0} |
|||
while (q.offsetParent){ |
|||
co.x += q.offsetLeft |
|||
co.y += q.offsetTop |
|||
q = q.offsetParent |
|||
} |
|||
return co |
|||
} |
|||
function coords(e) { |
|||
if (e.pageX !== undefined) |
|||
return {'x': e.pageX, 'y': e.pageY} |
|||
else // IE (buggy for v7 while zoomed, but who cares?) |
|||
return { |
|||
'x': e.clientX + document.documentElement.scrollLeft, |
|||
'y': e.clientY + document.documentElement.scrollTop |
|||
} |
|||
} |
|||
function boxMouseDown() { |
|||
if (dragHere && dragHere.parentNode) dragHere.parentNode.removeChild(dragHere) |
|||
dragGhost = this |
|||
dragGhost.id = 'voting-ghost' |
|||
dragOriginal = dragItem = this.parentNode |
|||
dragItem.className += ' dragitem' |
|||
drag = document.createElement('a') |
|||
drag.innerHTML = this.innerHTML |
|||
drag.id = 'voting-drag' |
|||
drag.className = 'cand' |
|||
cooD = absolutePosition(dragGhost) |
|||
cooD.x -= coo.x |
|||
cooD.y -= coo.y |
|||
mouseMove({'pageX': coo.x, 'pageY': coo.y}) |
|||
document.body.appendChild(drag) |
|||
disableSelection(document.body, true) |
|||
disableSelection(drag, true) |
|||
} |
|||
function mouseMove(e) { |
|||
coo = coords(e || window.event) |
|||
if (!drag) return |
|||
if (document.defaultView) // Opera: removing selection |
|||
document.defaultView.getSelection().removeAllRanges() |
|||
drag.style.left = (coo.x + cooD.x) + "px" |
|||
drag.style.top = (coo.y + cooD.y) + "px" |
|||
var lis = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
var dragCurrent |
|||
for (i=0; i<lis.length; i++) { |
|||
pos = absolutePosition(lis[i]) |
|||
if (pos.x <= coo.x && |
|||
pos.y <= coo.y && |
|||
pos.x + lis[i].offsetWidth > coo.x && |
|||
pos.y + lis[i].offsetHeight > coo.y) { |
|||
dragCurrent = lis[i] |
|||
} |
|||
} |
|||
if (!dragCurrent) { // pointer outside the whole list, returning ghost to its original location |
|||
dragCurrent = dragOriginal |
|||
} |
|||
if (dragCurrent != dragItem) { // changing target level |
|||
dragItem.className = dragItem.className.replace(' dragitem', '') |
|||
d = dragItem.getElementsByTagName('div')[0] |
|||
d.innerHTML = d.innerHTML.replace(/^([^:]+).*/, '$1') |
|||
dragItem.removeChild(dragGhost) |
|||
dragItem = dragCurrent |
|||
dragItem.className += ' dragitem' |
|||
if (dragItem.className.match(/groundwork/)) { |
|||
dragItem.getElementsByTagName('div')[0].innerHTML += ': ' + loc.newlevel |
|||
dragItem.appendChild(dragGhost) |
|||
} |
|||
else { // not an empty level, so inserting dragGhost in an appropriate place |
|||
var bef |
|||
var ch = dragItem.getElementsByTagName('a') |
|||
var spacer = dragItem.getElementsByTagName('span')[0] |
|||
if (ch.length == 0) { |
|||
bef = spacer |
|||
} else { |
|||
bef = ch[0] |
|||
for (i=0; i<ch.length; i++) |
|||
if (ch[i].innerHTML < dragGhost.innerHTML) |
|||
bef = ch[i+1] || spacer |
|||
} |
|||
dragItem.insertBefore(dragGhost, bef) |
|||
} |
|||
} |
|||
} |
|||
function spacer() { |
|||
var el = document.createElement('span') |
|||
el.className = 'spacer' |
|||
el.innerHTML = ' ' |
|||
return el |
|||
} |
|||
function mouseUp() { |
|||
if (!dragGhost || !drag || !dragItem) return |
|||
dragItem.className = dragItem.className.replace(' dragitem', '') |
|||
dragItem = null |
|||
dragGhost.id = '' |
|||
dragGhost = null |
|||
disableSelection(document.body, false) |
|||
disableSelection(drag, false) |
|||
document.body.removeChild(drag) |
|||
drag = null |
|||
// arranging structures |
|||
function groundwork() { |
|||
var el = document.createElement('li') |
|||
el.className = 'groundwork' |
|||
el.innerHTML = '<div class="li-id">!</div>' |
|||
return el |
|||
} |
|||
// creating groundworks around any newly created level |
|||
var lis = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
for (i=0; i<lis.length-1; i++) |
|||
if (lis[i].className == 'groundwork' && lis[i].getElementsByTagName('a').length > 0) { |
|||
lis[i].className = 'level' |
|||
lis[i].appendChild(spacer()) |
|||
lis[i].parentNode.insertBefore(groundwork(), lis[i].nextSibling) |
|||
lis[i].parentNode.insertBefore(groundwork(), lis[i]) |
|||
} |
|||
// destroying empty levels, except when there's only one |
|||
var lis = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
if (lis.length > 4) |
|||
for (i=0; i<lis.length-1; i++) |
|||
if (lis[i].className == 'level' && lis[i].getElementsByTagName('a').length == 0) |
|||
lis[i].parentNode.removeChild(lis[i]) |
|||
// blowing up inevitable duplicate groundworks |
|||
var lis = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
for (i=1; i<lis.length; i++) { |
|||
if (lis[i].className == 'groundwork' && lis[i-1].className == 'groundwork') |
|||
lis[i].parentNode.removeChild(lis[i]) |
|||
} |
|||
var lis = document.getElementById('schulze-list').getElementsByTagName('li') |
|||
// "drag first candidate here" label |
|||
if (lis[1].getElementsByTagName('a').length == 0) |
|||
lis[1].insertBefore(dragHere, lis[1].getElementsByTagName('span')[0]) |
|||
// renumbering |
|||
var j = 0 |
|||
for (i=0; i<lis.length-1; i++) { |
|||
lis[i].getElementsByTagName('div')[0].innerHTML = |
|||
lis[i].className == 'groundwork' |
|||
? (j==0 ? '…'+(++j) : (i==lis.length-2 ? ++j : j+'…'+(++j))) : j |
|||
} |
|||
} |
|||
document.getElementById('voting-container').innerHTML = '' |
|||
var btn = el('input') |
|||
btn.type = 'button' |
|||
btn.id = 'voting-button' |
|||
btn.value = loc.votebutton |
|||
btn.onclick = votingStart |
|||
var status = el('div') |
|||
status.id = 'voting-status' |
|||
document.getElementById('voting-container').appendChild(status) |
|||
document.getElementById('voting-container').appendChild(btn) |
|||
})() |
Текущая версия от 13:25, 1 августа 2024
/*
* Скрипт для выборов в Арбитражный комитет (ВП:АК)
*
* Авторы: Kalan, Serhio Magpie, stjn
*/
$(function () {
var containerEl = document.getElementById( 'voting-container' );
if ( containerEl === null ) {
mw.log.warn( 'arbcomVoting: Aborted because no voting container was detected.' );
return;
}
var wgUserName = mw.config.get( 'wgUserName' );
if ( wgUserName === null ) {
mw.log.warn( 'arbcomVoting: Not logged in.' );
return;
}
/******* COMMON ******* */
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] } }
/******* VARIABLES *******/
var data = require( './ondemand-arbcomVoting.json' );
var wgNamespaceNumber = mw.config.get( 'wgNamespaceNumber' );
var votes = {};
var saving = null;
var api;
var statusEl;
var btnEl;
/******* MAIN *******/
function votingLength( date, days ) {
var result = new Date( date );
result.setDate( result.getDate() + days );
return result;
}
function votingStart() {
$( '.voting-container' ).addClass( 'active' );
statusEl.textContent = 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.textContent = '';
$( '#voting-msg-criteriafail' ).show();
}
function votingContinue() {
statusEl.textContent = 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'
}).done(votingDraw);
}
function onClick( e ) {
e.preventDefault();
var button = e.currentTarget;
var span = button.querySelector( 'span' );
var tr = button.parentNode.parentNode;
var candidate = button.dataset.candidate;
var type = button.dataset.type;
var active = Boolean( button.getAttribute( 'aria-checked' ) );
var option = data.options[ type ];
// Update other button first
var otherButton = tr.querySelector( `.vote-button[aria-checked="true"]` );
var otherType = otherButton && otherButton.dataset.type;
if ( otherButton ) {
var otherVote = otherType === 'support' ? 1 : -1;
otherButton.setAttribute( 'aria-checked', false );
}
// Change vote
var vote = type === 'support' ? 1 : -1;
var sameVote = votes[ candidate ].value === vote;
votes[ candidate ].value = ( sameVote ? 0 : vote );
button.setAttribute( 'aria-checked', !sameVote );
// Update table row
tr.className = !sameVote ? 'vote-' + type : '';
}
function addVoteButton( candidate, type, previousVote ) {
var option = data.options[ type ];
var didVote = previousVote === ( type === 'support' ? 1 : -1 );
var votedOpposite = previousVote === ( type === 'support' ? -1 : 1 );
var voteType = votedOpposite ? 'changevote' : 'novote';
var button = document.createElement( 'button' );
button.className = 'vote-button skin-invert';
button.setAttribute( 'role', 'switch' );
button.setAttribute( 'type', 'button' );
button.setAttribute( 'data-candidate', candidate );
button.setAttribute( 'data-type', type );
if ( didVote ) {
button.setAttribute( 'data-voted', true );
}
button.setAttribute( 'aria-checked', didVote );
button.addEventListener( 'click', onClick );
var span = document.createElement( 'span' );
span.textContent = option[ voteType ];
if ( votedOpposite ) {
span.textContent = span.textContent.replace( '%candidate%', candidate );
}
button.title = span.textContent;
button.appendChild( span );
return button;
}
function votingDraw(d) {
statusEl.textContent = '';
btnEl.setLabel(data.strings.savebutton);
btnEl.setDisabled(false);
btnEl.off('click').on('click', votingSave);
btnEl.$element.css( 'display', '' );
var co = ( d.query && d.query.usercontribs ) || [];
// retrieving votes, according to contributions, only latest ones are valid
for ( var 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 match = false;
if ( co[ i ].title.startsWith( data.config.votepath ) ) {
match = co[ i ].title.match( /\/([+-])\/(.*?)$/ );
}
if ( match ) {
var hasSummary = co[ i ].comment === data.strings.summary;
if ( hasSummary ) {
votes[ match[ 2 ] ] = {
'orig': match[ 1 ] === '+' ? +1 : -1,
'value': 0
};
}
}
}
var div1 = document.createElement( 'div' );
div1.id = 'voting-standard';
var help = document.createElement( 'p' );
help.textContent = data.strings.votinghelp;
div1.appendChild( help );
var tab = document.createElement( 'table' );
tab.className = 'voting-table';
var caption = document.createElement( 'caption' );
caption.textContent = data.strings.votebutton;
tab.appendChild( caption );
for ( var candidate in votes ) {
if ( candidate.match( /^_[srl]$/ ) ) {
continue;
}
var tr = document.createElement( 'tr' );
var th = document.createElement( 'th' );
th.setAttribute( 'scope', 'row' );
var lin = document.createElement('a');
lin.href = mw.util.getUrl( data.config.talkpath + candidate );
lin.target = '_blank';
lin.textContent = candidate;
lin.title = data.strings.questionsTooltip.replace( '%candidate%', candidate );
th.appendChild( lin );
tr.appendChild( th );
// Support button
var td1 = document.createElement( 'td' );
td1.appendChild( addVoteButton( candidate, 'support', votes[ candidate ].orig ) );
tr.appendChild( td1 );
// Oppose button
var td2 = document.createElement( 'td' );
td2.appendChild( addVoteButton( candidate, 'oppose', votes[ candidate ].orig ) );
tr.appendChild( td2 );
tab.appendChild( tr );
}
div1.appendChild(tab);
statusEl.textContent = '';
$( '#voting-msg-justbeforesave' ).show();
statusEl.parentNode.insertBefore(div1, statusEl);
}
function showThankYou() {
containerEl.innerHTML = '';
$( '.voting-msg' ).hide();
$( '#voting-msg-thankyou' ).show();
}
function votingSave() {
if (!saving) {
var div = document.createElement('div');
div.className = 'voting-saving';
div.textContent = data.strings.saveprog;
div.insertAdjacentHTML( 'afterbegin', '<div class="voting-progress"><div class="voting-progress-progress" style="width:0%"> </div></div>' );
statusEl.parentNode.appendChild( div );
btnEl.setDisabled( true );
$( '#voting-standard' ).hide();
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 {
showThankYou();
}
} else {
if (saving.cursor === -5) {
saving.cursor = 0;
}
if (saving.cursor === -1) {
return;
}
document.querySelector( '.voting-progress-progress' ).style.width = 100*(saving.cursor+1)/saving.pages.length + '%';
api.postWithToken('edit', {
'action': 'edit',
'notminor': 1,
'unwatch': 1,
'assert': 'user',
'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;
showThankYou();
}
}
}
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);
// Init
if ( containerEl ) {
containerEl.innerHTML = '';
statusEl = document.createElement('div');
statusEl.className = 'voting-status voting-msg';
containerEl.appendChild( statusEl );
$( '.voting-msg a' ).attr( 'target', '_blank' );
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));
}
} );
});