Skip to content

Instantly share code, notes, and snippets.

@shu8
Last active August 29, 2015 14:13
Show Gist options
  • Save shu8/daae9127fa0fe06d5e4d to your computer and use it in GitHub Desktop.
Save shu8/daae9127fa0fe06d5e4d to your computer and use it in GitHub Desktop.
Userscript to add a bunch of optional 'features' to the StackExchange sites.
// ==UserScript==
// @name SE Extra, Optional Features
// @namespace http://stackexchange.com/users/4337810/%E1%B9%A7%D0%BD%CA%8A%C3%9F
// @version 0.6
// @description Adds a bunch of optional 'features' to the StackExchange sites.
// @author ṧнʊß (http://stackexchange.com/users/4337810/%E1%B9%A7%D0%BD%CA%8A%C3%9F)
// @match *://*.stackexchange.com/*
// @match *://*.stackoverflow.com/*
// @match *://*.superuser.com/*
// @match *://*.serverfault.com/*
// @match *://*.askubuntu.com/*
// @match *://*.stackapps.com/*
// @match *://*.mathoverflow.net/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js
// @require https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require https://cdn.rawgit.com/timdown/rangyinputs/master/rangyinputs-jquery-src.js
// @require https://cdn.rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @updateURL https://gist.github.com/shu8/daae9127fa0fe06d5e4d/raw/67668665eeda766f03e70b02fa6ac3901ccf620a/features.user.js
// ==/UserScript==
var functionsToCall = { //ALL the functions must go in here
grayOutVotes: function () { // For graying out votes AND vote count:
//alert('gray out votes');
if ($('.deleted-answer').length) {
$('.deleted-answer .vote-down-off, .deleted-answer .vote-up-off, .deleted-answer .vote-count-post').css('opacity', '0.5');
}
},
moveBounty: function () { // For moving bounty to the top:
//alert('move bounty');
if ($('.bounty-notification').length) {
$('.bounty-notification').insertAfter('.question .fw');
$('.question .bounty-notification .votecell').remove();
}
},
dragBounty: function () { // For draggable bounty window:
//alert('draggable');
$('.bounty-notification').click(function () {
setTimeout(function () {
$('#start-bounty-popup').draggable().css('cursor', 'move');
}, 50);
});
},
renameChat: function () { // For renaming Chat tabs:
//alert('rename chat');
if (window.location.href.indexOf('chat') > -1) {
document.title = 'Chat - ' + document.title;
}
},
exclaim: function () { // For remvoving exclamation mark:
//alert('exclaim');
var old = $("td.comment-actions > div > div > div.message-text");
var newText = old.text().replace("!", ".");
old.html(newText);
},
employeeStar: function () { // For looking for employees:
//alert('employee');
var employees = ["Jeff Atwood", "Joel Spolsky", "Jarrod Dixon", "Geoff Dalgas", "David Fullerton", "Korneel Bouman", "Robert Cartaino", "Kevin Montrose",
"MandyK", "Marc Gravell", "balpha", "Matt Sherman", "Danny Miller", "Jason Punyon", "NickC", "Kyle Brandt", "Jin", "Tall Jeff", "Zypher",
"Nick Craver", "Nick Larsen", "Shog9", "Greg", "Alex Miller", "GuyZee", "abby hairboat", "samthebrand", "Laura", "Grace Note", "Dimitar Stanimiroff",
"Hammer", "Peter Grace", "Charles", "Anna Lear", "stevvve", "Bethany", "Andrew17", "Kizzle", "Jay", "mjibson", "Stefan Schwarzgruber", "Will Cole", "Sean Bave",
"Robyn", "Bart Silverstrim", "Jaydles", "Maxwell Applebaum", "Snails", "Jordan Conner", "Bodhi", "cashenhu", "rb4", "Maurbawlz", "CMartin", "Joe Humphries", "Max",
"Oded", "Val Perez", "rossipedia", "Derek Still", "Tim Post", "Paul", "PDePree", "Sklivvz", "Todd Jenkins", "Jim Egan", "Kaziorex", "Ben Collins", "TomOnTime", "Dr.D",
"David", "Sara Rayman", "Monika P", "Prefontaine", "m0sa", "Jon Ericson", "Juice", "Tania", "Angela", "Hynes", "Kasra Rahjerdi", "Gabe", "Bret Copeland", "Arie Litovsky",
"Pops", "Megan Spaans", "Whitney Dwyer", "Philip Camillo", "onesysadmin", "Aurelien Gasser", "Alyssa Tomback", "Alex Cresswell", "couchand", "Brian Nickel", "Princess",
"Yaakov Ellis", "Ana Hevesi", "Noureddine Latrech", "Hertz", "Jill Ciaccia", "Tobias Schmidt", "Jon Chan", "Johanna Perrin", "Kristian Bright", "John LeClerc",
"Rob Dandorph", "Jessica Genther", "Courtny Cotten", "Stephanie", "Sean Durkin", "rla4", "Alex Warren", "Jaime Kronick", "Alexa", "Samuel Rouayrenc", "Josh Helfgott",
"Peter Tarr", "Shane Madden", "Nextraztus", "G-Wiz", "Dan O'Boyle", "yolovolo", "Griffin Sandberg", "ODB", "Mark Villarreal", "Lowell Gruman Jr.", "bweber", "Natalie How",
"Haney", "jmac", "Emmanuel Andem-Ewa", "Jess Pardue", "Dean Ward", "Steve Trout", "Nicholas Chabanovsky", "Kelli Ward", "Noah Neuman", "Lauren Roemer", "Heidi Hays",
"Joe Wilkie", "Mackenzie Ralston"];
$('.comment, .deleted-answer-info, .employee-name, .started, .user-details').each(function () { //normal comments, deleted answers (deleted by), SE.com/about, question feed users, question/answer/edit owners
var $divtext = $(this);
$.each(employees, function (index, value) {
if ($divtext.find('a[href*="/users"]').html() == value) {
$divtext.find('a[href*="/users"]').append('<span class="mod-flair" title="possible employee">&Star;</span>');
}
});
});
},
bulletReplace: function () { // For replacing disclosure bullets with normal ones:
//alert('bullets');
$('.dingus').each(function () {
$(this).html('&#x25cf;');
});
},
addEllipsis: function () { // For adding ellipsis to long names:
//alert('ellipsis');
$('.user-info .user-details').css('text-overflow', 'ellipsis');
},
moveCommentsLink: function () { // For adding the 'show x more comments' link before the commnents:
//alert('comments link');
$('.js-show-link.comments-link').each(function () {
var $this2 = $(this);
$("<tr><td></td><td>" + $this2.outerHTML() + "</td></tr>").insertBefore($(this).parent().closest('tr')).click(function () {
$(this).hide();
});
});
},
unHideAnswer: function () {
//alert('downvoted answer');
$(".downvoted-answer").hover(function () {
$(this).removeClass("downvoted-answer");
}, function () {
$(this).addClass("downvoted-answer");
});
},
fixedTopbar: function () {
//alert('topbar');
$('.topbar').css({
'position': 'fixed',
'z-index': '1'
});
},
highlightQuestions: function() {
setTimeout(function() { //Need delay to make sure the CSS is applied
//alert('highlight alternative');
if (window.location.href.indexOf('superuser') > -1) { //superuser
var betterCSS = {
backgroundColor: '#a1eaff',
color: 'black'
};
}
else if (window.location.href.indexOf('stackoverflow') > -1) { //stackoverflow
var betterCSS = {
backgroundColor: '#ffefc6',
borderWidth: '0'
};
}
else {//if (window.location.href.indexOf('.stackexchange.com') > -1) {
//if (window.location.href.indexOf('meta') === -1) { //beta sites
var betterCSS = {
backgroundColor: '#c3dafa',
borderWidth: '0'
};
//}
}
var interestingTagsDiv = $("#interestingTags").text();
var interesting = interestingTagsDiv.split(' ');
interesting.pop(); //Because there's one extra value at the end
$(".tagged-interesting > .summary > .tags > .post-tag").filter(function(index) {
return interesting.indexOf($(this).text()) > -1;
}).css(betterCSS);
$(".tagged-interesting").removeClass("tagged-interesting");
}, 100);
},
displayName: function() {
//alert('display name');
var uname = $('.gravatar-wrapper-24').attr('title');
var insertme = "<span class='reputation links-container' style='color:white;' title='"+uname+"'>"+uname+"</span>";
$(insertme).insertBefore('.gravatar-wrapper-24');
},
colorAnswerer: function() {
//alert('color answerer comments');
$('.answercell').each(function(i, obj) {
var x = $(this).find('.user-details a').text();
$('.answer .comment-user').each(function() {
if ($(this).text() == x) {
$(this).css('background-color', 'orange');
}
});
});
$('.js-show-link.comments-link').click(function() { //Keep CSS when 'show x more comments' is clicked
setTimeout(function() {
functionsToCall.colorAnswerer();
}, 500);
});
},
addBullets: function() {
var list = '- ' + $('[id^="wmd-input"]').getSelection().text.split('\n').join('\n- ');
$('[id^="wmd-input"]').replaceSelectedText(list);
},
addKbd: function() {
$('[id^="wmd-input"]').surroundSelectedText("<kbd>", "</kbd>");
},
kbdAndBullets: function() {
//alert('add kbd and list options');
var kbdBtn = $('<button/>', {
text: '<>',
class: 'wmd-button',
click: function (e) {
e.preventDefault();
functionsToCall.addKbd();
}
}).css('left', '400px');
var listBtn = $('<button/>', {
text: '-',
class: 'wmd-button',
click: function (e) {
e.preventDefault();
functionsToCall.addBullets();
}
}).css('left', '425px');
setTimeout(function() {
$('[id^="wmd-redo-button"]').after(kbdBtn);
$('[id^="wmd-redo-button"]').after(listBtn);
}, 500);
$('[id^="wmd-input"]').bind('keydown', 'alt+l', function () {
functionsToCall.addBullets();
});
$('[id^="wmd-input"]').bind('keydown', 'alt+k', function () {
functionsToCall.addKbd();
});
},
addCheckboxes: function() {
$('#reasons').remove(); //remove the div containing everything, we're going to add/remove stuff now:
if ($('[class^="inline-editor"]').length < 0) { //if there is no inline editor, do nothing
;
} else if (window.location.href.indexOf('/edit') > -1 || $('[class^="inline-editor"]').length > 0) { //everything else
$('.form-submit').before('<div id="reasons"></div>');
$.each(JSON.parse(GM_getValue('editReasons')), function (i, obj) {
$('#reasons').append('<label><input type="checkbox" value="' + this[1] + '"</input>' + this[0] + '</label>&nbsp;');
});
$('#reasons input[type="checkbox"]').css('vertical-align', '-2px');
$('#reasons label').hover(function () {
$(this).css({ //on hover
'background-color': 'gray',
'color': 'white'
});
}, function () {
$(this).css({ //on un-hover
'background-color': 'white',
'color': 'inherit'
});
});
var editCommentField = $('[id^="edit-comment"]');
$('#reasons input[type="checkbox"]').change(function () {
if (this.checked) { //Add it to the summary
if (editCommentField.val() == '') {
editCommentField.val(editCommentField.val() + $(this).val().replace(/on$/g, ''));
} else {
editCommentField.val(editCommentField.val() + '; ' + $(this).val().replace(/on$/g, ''));
}
var newEditComment = editCommentField.val(); //Remove the last space and last semicolon
editCommentField.val(newEditComment).focus();
} else if (!this.checked) { //Remove it from the summary
editCommentField.val(editCommentField.val().replace($(this).val() + '; ', '')); //for middle/beginning values
editCommentField.val(editCommentField.val().replace($(this).val(), '')); //for last value
}
});
}
},
displayDeleteValues: function() { //Display the items from list and add buttons to delete them
$('#currentValues').html(' ');
$.each(JSON.parse(GM_getValue('editReasons')), function (i, obj) {
$('#currentValues').append(this[0] + ' - ' + this[1] + '<input type="button" id="' + i + '" value="Delete"><br />');
});
functionsToCall.addCheckboxes();
},
editComment: function() {
//alert('edit comment');
var cssForDiv = {
'display': 'inline-block',
'position': 'fixed',
'width': '500px',
'top': '10px',
'left': '50%',
'z-index': '2',
'margin-left': '-250px',
'background-color': 'gray',
'color': 'white',
'-webkit-border-radius': '15px',
'-moz-border-radius': '15px',
'border-radius': '15px',
'text-align': 'center'
};
var div = "<div id='dialogEditReasons'><span id='closeDialogEditReasons' style='float:right;'>Close</span><span id='resetEditReasons' style='float:left;'>Reset</span> \
<h2>View/Remove Edit Reasons</h2> \
<div id='currentValues'></div> \
<br /> \
<h3>Add a custom reason</h3> \
Display Reason: <input type='text' id='displayReason'> \
<br /> \
Actual Reason: <input type='text' id='actualReason'> \
<br /> \
<input type='button' id='submitUpdate' value='Submit'> \
</div>";
$('body').append(div);
$('#dialogEditReasons').css(cssForDiv).draggable().hide();
$('#closeDialogEditReasons').css('cursor', 'pointer').click(function () {
$(this).parent().hide(500);
});
$('#resetEditReasons').css('cursor', 'pointer').click(function () {
var options = [ //Edit these to change the default settings
['formatting', 'Improved Formatting'],
['spelling', 'Corrected Spelling'],
['grammar', 'Fixed grammar'],
['greetings', 'Removed thanks/greetings']
];
if (confirm('Are you sure you want to reset the settings to Formatting, Spelling, Grammar and Greetings')) {
GM_setValue('editReasons', JSON.stringify(options));
alert('Reset options to default. Refreshing...');
location.reload();
}
});
if (GM_getValue('editReasons', -1) == -1) { //If settings haven't been set
$('#dialogEditReasons').show(); //Show the dialog
} else {
var options = JSON.parse(GM_getValue('editReasons')); //If they have, get the options
}
$('#dialogEditReasons').on('click', 'input[value="Delete"]', function () { //Click handler to delete when delete button is pressed
var delMe = $(this).attr('id');
options.splice(delMe, 1); //actually delete it
GM_setValue('editReasons', JSON.stringify(options)); //save it
functionsToCall.displayDeleteValues(); //display the items again (update them)
});
$('#submitUpdate').click(function () { //Click handler to update the array with custom value
if ($('#displayReason').val() == '' || $('#actualReason').val() == '') {
alert('Please enter something in both the textboxes!');
} else {
var arrayToAdd = [$('#displayReason').val(), $('#actualReason').val()];
options.push(arrayToAdd); //actually add the value to array
functionsToCall.displayDeleteValues(); //display the items again (update them)
GM_setValue('editReasons', JSON.stringify(options)); //Save the value
}
});
setTimeout(function() {
functionsToCall.addCheckboxes();
//Add the button to update and view the values in the help menu:
$('.topbar-dialog.help-dialog.js-help-dialog > .modal-content ul').append("<li><a href='javascript:void(0)' id='editReasonsLink'>Edit Reasons \
<span class='item-summary'>Edit your personal edit reasons for SE sites</span></a></li>");
$('#editReasonsLink').click(function () {
functionsToCall.displayDeleteValues();
$('#dialogEditReasons').show(500); //Show the dialog to view and update values
});
}, 500);
$('.post-menu > .edit-post').click(function () {
setTimeout(function () {
functionsToCall.addCheckboxes();
}, 500);
});
},
shareLinksMarkdown: function() {
//alert('change share value')
$('.short-link').click(function() {
setTimeout(function() {
$('.share-tip input').val('[' + $('#question-header a').html() + '](' + document.URL + ')');
}, 500);
});
},
commentShortcuts: function() {
$('.js-add-link.comments-link').click(function() {
setTimeout(function() {
$('.comments textarea').bind('keydown', 'ctrl+k', function(e) {
e.preventDefault();
$(this).surroundSelectedText('`', '`');
});
$('.comments textarea').bind('keydown', 'ctrl+i', function(e) {
e.preventDefault();
$(this).surroundSelectedText('*', '*');
});
$('.comments textarea').bind('keydown', 'ctrl+b', function(e) {
e.preventDefault();
$(this).surroundSelectedText('**', '**');
});
}, 200);
});
},
unspoil: function() {
$('#answers div[id*="answer"], div[id*="question"]').each(function() {
$(this).find('.post-menu').append('<span class="lsep">|</span><a id="showSpoiler-' + $(this).attr("id") + '" href="javascript:void(0)">unspoil</span>');
});
$('a[id*="showSpoiler"]').click(function() {
var x = $(this).attr('id').split(/-(.+)?/)[1];
$('#'+x+' .spoiler').removeClass('spoiler');
});
}
};
// Format for options below: <label><input type='checkbox' id='id'>Text</label><br />
var div = "<div id='featureGMOptions' style='display:inline-block; position:fixed; top:10px; left:50%; width:500px; margin-left:-250px; z-index:2; background-color:gray; color:white; padding: 10px; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px;'>\
<span id='closeFeature' style='float:right;'>Close</span> <span id='resetFeature' style='float:right;'>Reset&nbsp;</span> <span id='featureTitle'><h2>Extra Feature Options</h2></span> \
<label><input type='checkbox' id='grayOutVotes'/> Gray out deleted votes</label> <br /> \
<label><input type='checkbox' id='moveBounty'/> Move 'start bounty' to top</label> <br /> \
<label><input type='checkbox' id='dragBounty'/> Make bounty box draggable</label> <br /> \
<label><input type='checkbox' id='renameChat'/> Prepend 'Chat - ' to chat tabs' titles</label> <br /> \
<label><input type='checkbox' id='exclaim'/> Remove exclamation mark on message</label> <br /> \
<label><input type='checkbox' id='employeeStar'/> Add star after employee names</label> <br /> \
<label><input type='checkbox' id='bulletReplace'/> Replace triangluar bullets with normal ones</label> <br /> \
<label><input type='checkbox' id='addEllipsis'/> Add ellipsis to long names</label> <br /> \
<label><input type='checkbox' id='moveCommentsLink'/> Move 'show x more comments' to the top</label> <br /> \
<label><input type='checkbox' id='unHideAnswer'/> Un-gray-out downvoted answers</label> <br /> \
<label><input type='checkbox' id='fixedTopbar'/> Fix topbar position</label> <br /> \
<label><input type='checkbox' id='highlightQuestions'/> Alternate favourite questions highlighing</label> <br /> \
<label><input type='checkbox' id='displayName'/> Display name before gravatar</label> <br />\
<label><input type='checkbox' id='colorAnswerer'/> Color answerer's comments</label> <br />\
<label><input type='checkbox' id='kbdAndBullets'/> Add KBD and list buttons to editor toolbar</label> <br />\
<label><input type='checkbox' id='editComment'/> Pre-defined edit comment options</label> <br />\
<label><input type='checkbox' id='shareLinksMarkdown'/> Edit 'share' link to format of [post-name](url)</label> <br />\
<label><input type='checkbox' id='commentShortcuts'/> Use Ctrl+I,B,K (to italicise, bolden and add code backticks) in comments</label> <br />\
<label><input type='checkbox' id='unspoil'/> Add a link to show all spoilers in a post</label> <br />\
<input type='submit' id='submitOptions' value='Save settings' /><br /> \
</div>";
$('body').append(div);
$('#featureGMOptions').draggable().hide(); //Hide it at first
$('#featureTitle').css('cursor', 'move');
if (window.location.href.indexOf('/users/') > -1) { //Add the add features link
$('.sub-header-links.fr').append('<span class="lsep">|</span><a href="javascript:;" id="addFeaturesLink">add features</a>'); //Old profile (pre Feb-2015)
$('.additional-links').append('<span class="lsep">|</span><a href="javascript:;" id="addFeaturesLink">add features</a>'); //New profile (post Feb-2015) Currently on MSE only
$('#addFeaturesLink').click(function () {
$('#featureGMOptions').show(500);
});
}
$('#featureGMOptions > #closeFeature').css('cursor', 'pointer').click(function () {
$('#featureGMOptions').hide(500);
});
$('#featureGMOptions > #resetFeature').css('cursor', 'pointer').click(function () {
GM_deleteValue('featureOptions'); //Delete the setting when clicked
location.reload();
});
$(window).bind("load", function() {
if (GM_getValue("featureOptions", -1) != -1) { //If the setting is already set
var featureOptions = JSON.parse(GM_getValue("featureOptions"));
for (i = 0; i < featureOptions.length; ++i) {
$('#featureGMOptions #'+featureOptions[i]).prop('checked', true);
functionsToCall[featureOptions[i]](); //Call the functions that were chosen
}
} else { //If not, set it:
$('#featureGMOptions input').prop('checked', true);
$('#featureGMOptions').show(); //Show the dialog
var featureOptions = [];
}
$('#submitOptions').click(function () {
var featureOptions = [];
$('input[type=checkbox]:checked').each(function () {
var x = $(this).attr('id');
featureOptions.push(x); //Add the function's ID (also the checkbox's ID) to the array
});
GM_setValue('featureOptions', JSON.stringify(featureOptions)); //Save the setting
console.log('Options saved: ' + featureOptions);
$('#featureGMOptions').hide(500);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment