Skip to content

Instantly share code, notes, and snippets.

@GaurangTandon
Last active June 12, 2018 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GaurangTandon/860cbdef7355fa2978dc4bd3cb408981 to your computer and use it in GitHub Desktop.
Save GaurangTandon/860cbdef7355fa2978dc4bd3cb408981 to your computer and use it in GitHub Desktop.
Bold, italics, link, and custom shortcuts for comments in Stack Exchange
// ==UserScript==
// @name Comment Keyboard shortcuts
// @version 0.1
// @description Bold, italics, link, and custom shortcuts for comments in Stack Exchange
// @author Gaurang Tandon
// @match *://*.askubuntu.com/*
// @match *://*.mathoverflow.net/*
// @match *://*.serverfault.com/*
// @match *://*.stackapps.com/*
// @match *://*.stackexchange.com/*
// @match *://*.stackoverflow.com/*
// @match *://*.superuser.com/*
// @match *://chat.stackexchange.com/*
// @match *://chat.stackoverflow.com/*
// @exclude *://api.stackexchange.com/*
// @exclude *://blog.stackexchange.com/*
// @exclude *://blog.stackoverflow.com/*
// @exclude *://data.stackexchange.com/*
// @exclude *://elections.stackexchange.com/*
// @exclude *://openid.stackexchange.com/*
// @exclude *://stackexchange.com/*
// @grant none
// @history 0.1 - 9th June 2018 - Hello world!
// ==/UserScript==
/** BUGS:
* TODO: fix redundancy of insertTextWrapper and insertLink
*/
(function() {
'use strict';
/*NOTE: once you hit Enter and submit a comment, and then click "add comment" under the same post, the same textarea is again given to you unmodified as it was before*/
// (Ctrl +) keycode: ["placeholder", "delimiter"]
// you can easily extend this with your own wrappers
var WRAPPERS = {
66: ["strong text", "**"],
// use underscore for italics instead of asterisks to prevent
// clashes with bold
73: ["emphasized text", "_"],
75: ["enter code here", "`"]
};
function insertTextWrapper(commentBox, wrapper){
/*--- Expected behavior:
When there is some text selected: (unwrap it if already wrapped)
"]text[" --> "**]text[**"
"**]text[**" --> "]text["
"]**text**[" --> "**]**text**[**"
"**]**text**[**" --> "]**text**["
When there is no text selected:
"][" --> "**placeholder text**"
"**][**" --> ""
Note that `]` and `[` denote the selected text here.
*/
var selS = commentBox.selectionStart,
selE = commentBox.selectionEnd,
value = commentBox.value,
valBefore = value.substring(0, selS),
valMid = value.substring(selS, selE),
valAfter = value.substring(selE),
delimiter = wrapper[1],
delimLen = delimiter.length,
generatedWrapper,
// handle trailing spaces
trimmedSelection = valMid.match (/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/) || ["", "", "", ""];
// determine if text is currently wrapped
if(valBefore.endsWith(delimiter) && valAfter.startsWith(delimiter)){
commentBox.value = valBefore.substring(0, valBefore.length - delimLen) + valMid + valAfter.substring(delimLen);
commentBox.selectionStart = valBefore.length - delimLen;
commentBox.selectionEnd = (valBefore + valMid).length - delimLen;
commentBox.focus();
}else{
valBefore += trimmedSelection[1];
valAfter = trimmedSelection[3] + valAfter;
valMid = trimmedSelection[2];
generatedWrapper = delimiter + (valMid || wrapper[0]) + delimiter;
commentBox.value = valBefore + generatedWrapper + valAfter;
commentBox.selectionStart = valBefore.length + delimiter.length;
commentBox.selectionEnd = (valBefore + generatedWrapper).length - delimiter.length;
commentBox.focus();
}
}
function insertLink(commentBox){
var selS = commentBox.selectionStart,
selE = commentBox.selectionEnd,
value = commentBox.value,
valBefore = value.substring(0, selS),
valMid = value.substring(selS, selE),
valAfter = value.substring(selE),
placeholder = "enter link description here",
trimmedSelection = valMid.match (/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/) || ["", "", "", ""],
insertedMiddleText,
link = prompt("Insert Hyperlink http://example.com/");
valBefore += trimmedSelection[1];
valAfter = trimmedSelection[3] + valAfter;
valMid = trimmedSelection[2];
if(!/^http/.test(link)) link = "http://" + link;
insertedMiddleText = valMid || placeholder
commentBox.value = valBefore + "[" + insertedMiddleText + "](" + link + ")" + valAfter;
commentBox.selectionStart = valBefore.length + 1;
commentBox.selectionEnd = (valBefore + insertedMiddleText).length + 1;
commentBox.focus();
}
function attachHandlers(commentBox){
commentBox.onkeydown = function handleKeyDown(event){
var kC = event.keyCode;
if(!(event.ctrlKey || event.metaKey)) return true;
if(kC === 76){ // L
event.preventDefault();
insertLink(commentBox);
return;
}
if(WRAPPERS[kC]) {
event.preventDefault();
insertTextWrapper(commentBox, WRAPPERS[kC]);
}
};
}
setInterval(function(){
// first query for SE, second for chat
var commentBoxes = document.querySelectorAll(".js-comment-text-input:not(.key-shortcut-processed), textarea#input:not(.key-shortcut-processed)"), commentBox;
for(var i = 0, len = commentBoxes.length; i < len; i++){
commentBox = commentBoxes[i];
commentBox.classList.add("key-shortcut-processed");
attachHandlers(commentBox);
}
}, 500);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment