Skip to content

Instantly share code, notes, and snippets.

@poke
Last active March 12, 2019 20:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save poke/9058895 to your computer and use it in GitHub Desktop.
Save poke/9058895 to your computer and use it in GitHub Desktop.
[User script] StackExchange Election: Primary counter – Support on Stack Apps: http://stackapps.com/questions/4548
// ==UserScript==
// @id stackexchange-election-primary-counter@poke
// @name StackExchange Election: Primary counter
// @namespace poke
// @version 1.5.2
// @author Patrick Westerhoff
// @match *://*.stackoverflow.com/election*
// @match *://*.stackexchange.com/election*
// @match *://*.askubuntu.com/election*
// @match *://*.mathoverflow.net/election*
// @match *://*.serverfault.com/election*
// @match *://*.stackapps.com/election*
// @match *://*.superuser.com/election*
// @updateURL https://gist.githubusercontent.com/poke/9058895/raw/stackexchange-election-primary-counter.user.js
// @run-at document-end
// ==/UserScript==
(function () {
var primaryLink = document.querySelector('#tabs > a[href$="primary"]');
if (!primaryLink || !primaryLink.classList.contains('youarehere'))
return;
function addRow (table, cols, isHeader) {
var row = document.createElement('tr');
cols.forEach(function (cell) {
if (typeof cell !== 'object') {
cell = document.createTextNode(cell);
}
row.appendChild(document.createElement(isHeader ? 'th' : 'td')).appendChild(cell);
});
return table.appendChild(row);
}
// styles
var style = document.createElement('style');
style.textContent = '#_primary-counter { border: 1px solid silver; margin-bottom: 1em;}' +
'#_primary-counter th { border-bottom: 1px solid silver; }' +
'#_primary-counter td, #_primary-counter th { padding: 1px .5em; }' +
'#_primary-counter tr :nth-child(n+3) { text-align: right; }' +
'#_primary-counter tr:nth-child(11) { border-bottom: 1px solid silver; }' +
'#_primary-counter tr.withdrawn { color: #999; }';
document.head.appendChild(style);
// query candidate data
var users = Array.prototype.map.call(document.querySelectorAll('#mainbar .candidate-row'), function (post) {
var scores = post.querySelector('.candidate-score-breakdown');
return {
name: post.querySelector('.owner .user-info .user-details > a').textContent,
id: post.id.substr(5),
href: '#' + post.id,
votes: parseInt(post.querySelector('.votecell .js-vote-count').textContent, 10),
score: scores.querySelector('b').textContent.match(/(\d+\/\d+)/)[1],
rep: scores.querySelector('ul > li:nth-child(1)').textContent.trim().match(/reputation (.+)/)[1],
modBadges: scores.querySelector('ul > li:nth-child(2)').textContent.match(/moderation badges: (\d)\/\d/)[1],
editBadges: scores.querySelector('ul > li:nth-child(3)').textContent.match(/editing badges: (\d)\/\d/)[1],
participationBadges: scores.querySelector('ul > li:nth-child(4)').textContent.match(/participation badges: (\d)\/\d/)[1],
memberFor: post.querySelector('.user-details').lastChild.textContent.trim().match(/member for (.+)/)[1],
withdrawn: !!post.querySelector('.withdraw-date')
};
});
users.sort(function (a, b) { return b.votes - a.votes; });
// vote count querying
var mayQueryVoteCounts = document.body.innerHTML.includes('StackExchange.vote.election_init(true');
function queryVoteCounts (evt) {
evt.preventDefault();
var xhr = new XMLHttpRequest();
xhr.open('get', '/posts/' + this.id + '/vote-counts?_=' + (+new Date()), true);
xhr.onload = function () {
if (this.status == 200) {
var data = JSON.parse(this.response);
evt.target.parentNode.innerHTML = '+' + (+Math.abs(data.up)) + ' / ' + (-Math.abs(data.down));
}
};
xhr.send(null);
}
// display data
var table = document.createElement('table');
table.id = '_primary-counter';
addRow(table, ['#', 'User', 'Votes', 'Score', 'Reputation', 'Badges (M/E/P)', 'Member for'], true);
users.forEach(function (user, index) {
var link = document.createElement('a')
link.href = user.href;
link.appendChild(document.createTextNode(user.name));
var votes;
if (mayQueryVoteCounts) {
votes = document.createElement('a');
votes.addEventListener('click', queryVoteCounts.bind(user));
}
else {
votes = document.createElement('span');
}
votes.appendChild(document.createTextNode(user.votes));
var badges = [user.modBadges, user.editBadges, user.participationBadges].join(' / ');
var row = addRow(table, [index + 1, link, votes, user.score, user.rep, badges, user.memberFor]);
row.className = user.withdrawn ? 'withdrawn' : '';
});
document.querySelector('.question-status').appendChild(table);
})();
@mjpieters
Copy link

New version, updated for the 2015 elections:

// ==UserScript==
// @id             stackexchange-election-primary-counter@poke
// @name           StackExchange Election: Primary counter
// @namespace      poke
// @version        1.2.0
// @author         Patrick Westerhoff
// @include        http://stackoverflow.com/election/*
// @include        http://stackoverflow.com/election
// @updateURL      https://gist.github.com/poke/9058895/raw/stackexchange-election-primary-counter.user.js
// @run-at         document-end
// ==/UserScript==
(function () {
    var primaryLink = document.querySelector('#tabs > a[href$="primary"]');
    if (!primaryLink || primaryLink.className != 'youarehere')
        return;

    function addRow (table, cols, isHeader) {
        var row = document.createElement('tr');
        cols.forEach(function (cell) {
            if (typeof cell !== 'object') {
                cell = document.createTextNode(cell);
            }
            row.appendChild(document.createElement(isHeader ? 'th' : 'td')).appendChild(cell);
        });
        return table.appendChild(row);
    }

    // styles
    var style = document.createElement('style');
    style.textContent = '#_primary-counter { border: 1px solid silver; margin-bottom: 1em;}' +
        '#_primary-counter th { border-bottom: 1px solid silver; }' +
        '#_primary-counter td, #_primary-counter th { padding: 1px .5em; }' +
        '#_primary-counter tr :nth-child(n+3) { text-align: right; }' +
        '#_primary-counter tr:nth-child(11) { border-bottom: 1px solid silver; }' +
        '#_primary-counter tr.withdrawn { color: #999; }';
    document.head.appendChild(style);

    // query candidate data
    var users = Array.prototype.map.call(document.querySelectorAll('#mainbar > table > tbody > tr'), function (post) {
        return {
            name: post.querySelector('td.owner .user-info .user-details > a').textContent,
            href: '#' + post.id,
            votes: parseInt(post.querySelector('.votecell .vote-count-post').textContent, 10),
            score: post.querySelector('.user-info .candidate-score-breakdown span').textContent.substr(16),
            withdrawn: !!post.querySelector('.withdraw-date')
        };
    });
    users.sort(function (a, b) { return b.votes - a.votes; });

    // display data
    var table = document.createElement('table');
    table.id = '_primary-counter';
    addRow(table, ['#', 'User', 'Votes', 'Candidate score'], true);
    users.forEach(function (user, index) {
        var link = document.createElement('a')
        link.href = user.href;
        link.appendChild(document.createTextNode(user.name));
        var row = addRow(table, [index + 1, link, user.votes, user.score]);
        row.className = user.withdrawn ? 'withdrawn' : '';
    });
    document.querySelector('.question-status').appendChild(table);
})();

@samliew
Copy link

samliew commented Mar 5, 2019

'.votecell .vote-count-post' needs to be changed to '.votecell .js-vote-count' for the new post layout on L45

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment