MediaWiki:Gadget-ondemand-arbcomVoting.js: различия между версиями

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Содержимое удалено Содержимое добавлено
fix
поддержка тёмной темы
 
(не показано 16 промежуточных версий 1 участника)
Строка 45: Строка 45:


function votingStart() {
function votingStart() {
containerEl.classList.add( 'active' );
$( '.voting-container' ).addClass( 'active' );
statusEl.innerHTML = data.strings.statuscriteria;
statusEl.textContent = data.strings.statuscriteria;
btnEl.setDisabled(true);
btnEl.setDisabled(true);
btnEl.$element.css( 'display', 'none' );
btnEl.$element.css( 'display', 'none' );
Строка 84: Строка 84:


function votingCriteriaFail() {
function votingCriteriaFail() {
statusEl.innerHTML = data.strings.criteriafail;
statusEl.textContent = '';
$( '#voting-msg-criteriafail' ).show();
}
}


function votingContinue() {
function votingContinue() {
statusEl.innerHTML = data.strings.loadingvotes;
statusEl.textContent = data.strings.loadingvotes;
// latest contributions in ns:4
// latest contributions in ns:4
api.get({
api.get({
Строка 96: Строка 97:
'uclimit': 500,
'uclimit': 500,
'ucuser': wgUserName,
'ucuser': wgUserName,
'ucend': tstamp(data.config.start),
'ucend': tstamp( data.config.start ),
'ucdir': 'older',
'ucdir': 'older'
'prop': 'revisions',
'ucprop': 'comment'
}).done(votingDraw);
}).done(votingDraw);
}
}
Строка 138: Строка 137:


var button = document.createElement( 'button' );
var button = document.createElement( 'button' );
button.className = 'vote-button';
button.className = 'vote-button skin-invert';
button.setAttribute( 'role', 'switch' );
button.setAttribute( 'role', 'switch' );
button.setAttribute( 'type', 'button' );
button.setAttribute( 'type', 'button' );
Строка 161: Строка 160:


function votingDraw(d) {
function votingDraw(d) {
statusEl.innerHTML = '';
statusEl.textContent = '';
btnEl.setLabel(data.strings.savebutton);
btnEl.setLabel(data.strings.savebutton);
btnEl.setDisabled(false);
btnEl.setDisabled(false);
Строка 167: Строка 166:
btnEl.$element.css( 'display', '' );
btnEl.$element.css( 'display', '' );


var co = d.query && d.query.usercontribs;
var co = ( d.query && d.query.usercontribs ) || [];


// retrieving votes, according to contributions, only latest ones are valid
// retrieving votes, according to contributions, only latest ones are valid
for (i=0; i < data.candidates.length; i++) {
for ( var i = 0; i < data.candidates.length; i++ ) {
votes[data.candidates[i]] = {'orig':0, 'value':0};
votes[ data.candidates [ i ] ] = { 'orig': 0, 'value': 0 };
}
}
for ( var i = co.length - 1; i >= 0; i-- ) {
for ( var i = co.length - 1; i >= 0; i-- ) {
Строка 181: Строка 180:
var hasSummary = co[ i ].comment === data.strings.summary;
var hasSummary = co[ i ].comment === data.strings.summary;
if ( hasSummary ) {
if ( hasSummary ) {
votes[m[2]] = {
votes[ match[ 2 ] ] = {
'orig': m[ 1 ] === '+' ? +1 : -1,
'orig': match[ 1 ] === '+' ? +1 : -1,
'value': 0
'value': 0
};
};
Строка 191: Строка 190:
var div1 = document.createElement( 'div' );
var div1 = document.createElement( 'div' );
div1.id = 'voting-standard';
div1.id = 'voting-standard';
var help = document.createElement( 'p' );
div1.innerHTML = data.strings.votinghelp;
help.textContent = data.strings.votinghelp;
div1.appendChild( help );
var tab = document.createElement( 'table' );
var tab = document.createElement( 'table' );
tab.className = 'voting-table';
tab.className = 'voting-table';
Строка 230: Строка 231:
div1.appendChild(tab);
div1.appendChild(tab);


statusEl.innerHTML = data.strings.justbeforesave;
statusEl.textContent = '';
$( '#voting-msg-justbeforesave' ).show();
statusEl.parentNode.insertBefore(div1, statusEl);
statusEl.parentNode.insertBefore(div1, statusEl);
}
}
function showThankYou() {
containerEl.innerHTML = '';
$( '.voting-msg' ).hide();
$( '#voting-msg-thankyou' ).show();
}
function votingSave() {
function votingSave() {
if (!saving) {
if (!saving) {
var div = document.createElement('div');
var div = document.createElement('div');
div.id = 'voting-saving';
div.className = 'voting-saving';
div.className = div.id;
div.textContent = data.strings.saveprog;
div.innerHTML = '<div class="voting-progress"><div class="voting-progress-progress" style="width:0%">&nbsp;</div></div>' + data.strings.saveprog;
div.insertAdjacentHTML( 'afterbegin', '<div class="voting-progress"><div class="voting-progress-progress" style="width:0%">&nbsp;</div></div>' );
statusEl.parentNode.appendChild(div);
statusEl.parentNode.appendChild( div );
btnEl.setDisabled(true);
btnEl.setDisabled( true );
document.getElementById('voting-standard').style.visibility = 'hidden';
$( '#voting-standard' ).hide();


saving = {'cursor': 0, 'pages': []};
saving = { 'cursor': 0, 'pages': [] };


for (var i in votes) {
for (var i in votes) {
Строка 258: Строка 267:
votingSave();
votingSave();
} else {
} else {
showThankYou();
containerEl.innerHTML = data.strings.thankyou;
}
}
} else {
} else {
Строка 274: Строка 283:
'notminor': 1,
'notminor': 1,
'unwatch': 1,
'unwatch': 1,
'assert': 'user',
'summary': data.strings.summary,
'summary': data.strings.summary,
'title': saving.pages[saving.cursor].page,
'title': saving.pages[saving.cursor].page,
Строка 281: Строка 291:
if (!saving.pages[saving.cursor]) {
if (!saving.pages[saving.cursor]) {
saving.cursor = -1;
saving.cursor = -1;
showThankYou();
containerEl.innerHTML = data.strings.thankyou;
}
}
}
}
Строка 302: Строка 312:
data.config.votepath = data.config.votepath.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);
data.config.voteexceptionspath = data.config.voteexceptionspath.replace('%config.path%', data.config.path);
// Prepare strings
data.strings.justbeforesave = data.strings.justbeforesave.replace('%config.talkpagepath%', data.config.talkpagepath);
// Init
// Init
if ( containerEl ) {
if ( containerEl ) {
containerEl.className = containerEl.id;
containerEl.innerHTML = '';
containerEl.innerHTML = '';


statusEl = document.createElement('div');
statusEl = document.createElement('div');
statusEl.id = 'voting-status';
statusEl.className = 'voting-status voting-msg';
containerEl.appendChild( statusEl );
statusEl.className = statusEl.id;
containerEl.appendChild(statusEl);
$( '.voting-msg a' ).attr( 'target', '_blank' );


btnEl = new OO.ui.ButtonWidget( {
btnEl = new OO.ui.ButtonWidget( {

Текущая версия от 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%">&nbsp;</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));
		}
	} );
});