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