// ==UserScript==
// @name StackOverflow close votes shortcuts
// @namespace https://github.com/kappa7194/stackoverflow-close-votes-shortcuts
// @version 1.0.0-rc.2
// @description A script to add keyboard shortcuts to StackOverflow's close votes review queue
// @author Albireo
// @match http://stackoverflow.com/review/close*
// @grant none
// ==/UserScript==
(function () {
'use strict';
$(document).ready(function () {
var keys = {
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
'0': 48
};
var configuration = {
'actions': {
'leaveOpen': { 'key': '1', 'value': '8' },
'close': { 'key': '2', 'value': '6' },
'edit': { 'key': '3', 'value': '5' },
'skip': { 'key': '4', 'value': '1' }
},
'closeReasons': {
'duplicate': { 'key': '1', 'value': 'Duplicate' },
'offTopic': { 'key': '2', 'value': 'OffTopic' },
'unclear': { 'key': '3', 'value': 'Unclear' },
'tooBroad': { 'key': '4', 'value': 'TooBroad' },
'opinionBased': { 'key': '5', 'value': 'OpinionBased' }
},
'offTopicReasons': {
'superUser': { 'key': '1', 'value': '4' },
'serverFault': { 'key': '2', 'value': '7' },
'recommend': { 'key': '3', 'value': '16' },
'minimalProgram': { 'key': '4', 'value': '13' },
'typo': { 'key': '5', 'value': '11' },
'migration': { 'key': '6', 'value': '2' },
'other': { 'key': '7', 'value': '3' },
},
'migrationReasons': {
'meta': { 'key': '1', 'value': 'meta.stackoverflow.com' },
'superUser': { 'key': '2', 'value': 'superuser.com' },
'tex': { 'key': '3', 'value': 'tex.stackexchange.com' },
'dba': { 'key': '4', 'value': 'dba.stackexchange.com' },
'stats': { 'key': '5', 'value': 'stats.stackexchange.com' },
}
};
(function () {
var states = {
atQuestion: 1,
atCloseReason: 2,
atDuplicate: 3,
atOffTopic: 4,
atOtherSite: 5
};
var state = states.atQuestion;
var clickElement = function (selector) {
$(selector).focus().click();
};
var clickAction = function (action) {
clickElement('.review-actions [data-result-type="' + action + '"]');
};
var clickCloseReason = function (reason) {
clickElement('[name="close-reason"][value="' + reason + '"]');
};
var clickOffTopicReason = function (reason) {
clickElement('[name="close-as-off-topic-reason"][value="' + reason + '"]');
};
var clickOtherSite = function (site) {
clickElement('[name="migration"][value="' + site + '"]');
};
var keyHandler = function(key) {
switch (state) {
case states.atQuestion:
actionHandler(key);
break;
case states.atCloseReason:
closeReasonHandler(key);
break;
case states.atOffTopic:
offTopicHandler(key);
break;
case states.atOtherSite:
otherSiteHandler(key);
break;
}
};
var actionHandler = function (key) {
switch (key) {
case keys[configuration.actions.leaveOpen.key]:
clickAction(configuration.actions.leaveOpen.value);
resetState();
break;
case keys[configuration.actions.close.key]:
state = states.atCloseReason;
clickAction(configuration.actions.close.value);
break;
case keys[configuration.actions.edit.key]:
clickAction(configuration.actions.edit.value);
resetState();
break;
case keys[configuration.actions.skip.key]:
clickAction(configuration.actions.skip.value);
resetState();
break;
}
};
var closeReasonHandler = function (key) {
switch (key) {
case keys[configuration.closeReasons.duplicate.key]:
clickCloseReason(configuration.closeReasons.duplicate.value);
state = states.atDuplicate;
break;
case keys[configuration.closeReasons.offTopic.key]:
clickCloseReason(configuration.closeReasons.offTopic.value);
state = states.atOffTopic;
break;
case keys[configuration.closeReasons.unclear.key]:
clickCloseReason(configuration.closeReasons.unclear.value);
break;
case keys[configuration.closeReasons.tooBroad.key]:
clickCloseReason(configuration.closeReasons.tooBroad.value);
break;
case keys[configuration.closeReasons.opinionBased.key]:
clickCloseReason(configuration.closeReasons.opinionBased.value);
break;
}
};
var offTopicHandler = function (key) {
switch (key) {
case keys[configuration.offTopicReasons.superUser.key]:
clickOffTopicReason(configuration.offTopicReasons.superUser.value);
break;
case keys[configuration.offTopicReasons.serverFault.key]:
clickOffTopicReason(configuration.offTopicReasons.serverFault.value);
break;
case keys[configuration.offTopicReasons.recommend.key]:
clickOffTopicReason(configuration.offTopicReasons.recommend.value);
break;
case keys[configuration.offTopicReasons.minimalProgram.key]:
clickOffTopicReason(configuration.offTopicReasons.minimalProgram.value);
break;
case keys[configuration.offTopicReasons.typo.key]:
clickOffTopicReason(configuration.offTopicReasons.typo.value);
break;
case keys[configuration.offTopicReasons.migration.key]:
state = states.atOtherSite;
clickOffTopicReason(configuration.offTopicReasons.migration.value);
break;
case keys[configuration.offTopicReasons.other.key]:
clickOffTopicReason(configuration.offTopicReasons.other.value);
break;
}
};
var otherSiteHandler = function (key) {
switch (key) {
case keys[configuration.migrationReasons.meta.key]:
clickOtherSite(configuration.migrationReasons.meta.value);
break;
case keys[configuration.migrationReasons.superUser.key]:
clickOtherSite(configuration.migrationReasons.superUser.value);
break;
case keys[configuration.migrationReasons.tex.key]:
clickOtherSite(configuration.migrationReasons.tex.value);
break;
case keys[configuration.migrationReasons.dba.key]:
clickOtherSite(configuration.migrationReasons.dba.value);
break;
case keys[configuration.migrationReasons.stats.key]:
clickOtherSite(configuration.migrationReasons.stats.value);
break;
}
};
var resetState = function () {
state = states.atQuestion;
};
$(document).on('click', '#popup-close-question .popup-close a', function () {
resetState();
});
$(document).on('click', '#popup-close-question .popup-submit', function () {
resetState();
});
$(document).on('keyup', function (e) {
if (e.keyCode === 27) {
resetState();
return;
}
if ((e.target.tagName === 'INPUT' && e.target.type === 'text') || e.target.tagName === 'TEXTAREA') {
return;
}
keyHandler(e.keyCode);
});
})();
(function () {
var lookup = { };
lookup[configuration.actions.leaveOpen.value] = configuration.actions.leaveOpen.key;
lookup[configuration.actions.close.value] = configuration.actions.close.key;
lookup[configuration.actions.edit.value] = configuration.actions.edit.key;
lookup[configuration.actions.skip.value] = configuration.actions.skip.key;
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
for (var i = 0, j = mutation.addedNodes.length; i < j; i++) {
var node = $(mutation.addedNodes[i]);
if (node.prop('tagName') !== 'INPUT' || node.prop('type') !== 'button') {
continue;
}
node.val('[' + lookup[node.data('result-type')] + '] ' + node.val());
}
});
});
observer.observe(document.querySelector('.review-actions'), { 'childList': true });
})();
(function () {
var addSiblingHelper = function (root, selector, key) {
var element = $(root).find(selector).next();
element.html('[' + key + '] ' + element.html());
};
var addCousinHelper = function (root, selector, key) {
var element = $(root).find(selector).parent().next().next();
element.html('[' + key + '] ' + element.html());
};
var addCloseReasonHelper = function (root, reason, key) {
addSiblingHelper(root, '[name="close-reason"][value="' + reason + '"]', key);
};
var addOffTopicReasonHelper = function (root, reason, key) {
addSiblingHelper(root, '[name="close-as-off-topic-reason"][value="' + reason +'"]', key);
};
var addMigrationHelper = function (root, reason, key) {
addCousinHelper(root, '[name="migration"][value="' + reason + '"]', key);
};
var addHelpers = function (root) {
addCloseReasonHelper(root, configuration.closeReasons.duplicate.value, configuration.closeReasons.duplicate.key);
addCloseReasonHelper(root, configuration.closeReasons.offTopic.value, configuration.closeReasons.offTopic.key);
addCloseReasonHelper(root, configuration.closeReasons.unclear.value, configuration.closeReasons.unclear.key);
addCloseReasonHelper(root, configuration.closeReasons.tooBroad.value, configuration.closeReasons.tooBroad.key);
addCloseReasonHelper(root, configuration.closeReasons.opinionBased.value, configuration.closeReasons.opinionBased.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.superUser.value, configuration.offTopicReasons.superUser.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.serverFault.value, configuration.offTopicReasons.serverFault.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.recommend.value, configuration.offTopicReasons.recommend.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.minimalProgram.value, configuration.offTopicReasons.minimalProgram.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.typo.value, configuration.offTopicReasons.typo.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.migration.value, configuration.offTopicReasons.migration.key);
addOffTopicReasonHelper(root, configuration.offTopicReasons.other.value, configuration.offTopicReasons.other.key);
addMigrationHelper(root, configuration.migrationReasons.meta.value, configuration.migrationReasons.meta.key);
addMigrationHelper(root, configuration.migrationReasons.superUser.value, configuration.migrationReasons.superUser.key);
addMigrationHelper(root, configuration.migrationReasons.tex.value, configuration.migrationReasons.tex.key);
addMigrationHelper(root, configuration.migrationReasons.dba.value, configuration.migrationReasons.dba.key);
addMigrationHelper(root, configuration.migrationReasons.stats.value, configuration.migrationReasons.stats.key);
};
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
for (var i = 0, j = mutation.addedNodes.length; i < j; i++) {
var node = mutation.addedNodes[i];
if (node.tagName === 'DIV' && node.id === 'popup-close-question') {
addHelpers(node);
}
}
});
});
observer.observe(document.querySelector('.review-content'), { 'childList': true, 'subtree': true });
})();
});
})();