// ==UserScript== // @name Stack Exchange comments formatting shortcuts // @namespace stackexchange // @version 1.3 // @description Adds Ctrl+B (bold), Ctrl+I (italic), Ctrl+K (code), and Ctrl+L (link) keyboard shortcuts to comments. // @icon http://i.stack.imgur.com/6xzO1.png // @homepage http://stackapps.com/q/2103/5418 // @author Kip Robinson - http://stackoverflow.com/users/18511/kip // @contributor Rodolfo Buaiz - http://stackapps.com/users/10590/brasofilo // @match *://*.askubuntu.com/* // @match *://*.mathoverflow.net/* // @match *://*.serverfault.com/* // @match *://*.stackapps.com/* // @match *://*.stackexchange.com/* // @match *://*.stackoverflow.com/* // @match *://*.superuser.com/* // @exclude *://chat.* // @exclude *://blog.* // @exclude *://api.* // @exclude *://data.* // @updateURL https://gist.github.com/raw/a539c1905608b253fcb6/CommentsFormattingShortcuts.user.js // @downloadURL https://gist.github.com/raw/a539c1905608b253fcb6/CommentsFormattingShortcuts.user.js // @grant none // ==/UserScript== if( filter_so_check_page() ) { with_jquery( start_up ); } /** * Check current page to determine if needs to run */ function filter_so_check_page() { var will_run = false; if( StackExchange ) { var routes = ['Review/Task', 'Questions/Show']; var route = StackExchange.options.routeName; if( ( routes.indexOf(route) !== -1 ) ) { will_run = true; } } return will_run; } /** * Inject the script into the page */ function with_jquery( callback ) { var script = document.createElement("script"); script.type = "text/javascript"; script.textContent = "(" + callback.toString() + ")(jQuery)"; document.body.appendChild( script ); } /** * The real deal */ function start_up( $ ) { $(document).on( 'keydown', 'textarea[name=comment]', function(e) { //Ctrl+[BIKL] if( (e.ctrlKey||e.metaKey) && !e.altKey && (e.which == 66 || e.which == 73 || e.which == 75 || e.which == 76)) { //all text var text = $(this).val(); //text before selection var before = text.substring(0,this.selectionStart); //selectect text var selected = text.substring(this.selectionStart,this.selectionEnd); //text after selection var after = text.substring(this.selectionEnd,text.length); //length of selection var selLen = selected.length; //markup character that will go before/after section. (Note: link is handled a bit differently.) var markup = ''; var isLink = false; if(e.which == 66) markup = '**'; else if(e.which == 73) markup = '*'; else if(e.which == 75) markup = '`'; else isLink = true; //markup length var mLen = markup.length; //replace is what the selected text will be replaced with var replace = ''; if( selLen == 0 ) { //nothing selected. just print the markup if(isLink) { var url = prompt('Please input link URL'); var linkText = prompt('Please input link text'); replace = '[' + linkText + '](' + url + ')'; } else { replace = markup; } } else if( !isLink && selLen > 2*mLen && selected.substring(0,mLen) == markup && selected.substring(selLen - mLen, selLen) == markup ) { //We have selected something that starts and ends with the markup. We will remove the markup in //this case. For example, "*sometext*" becomes just "sometext". This is not available for links. replace = selected.substring(mLen, selLen - mLen); } else { //we have selected something. put the markup before and after it. if(isLink) { var url = prompt('Please input link URL'); replace = '[' + selected + '](' + url + ')'; } else { replace = markup + selected + markup; } } //now update the text $(this).val(before + replace + after); if(selLen > 0) { //if something was selected, make the result selected too. this.selectionStart = before.length; this.selectionEnd = before.length + replace.length; } else { //nothign was selected, so put the cursor at the end of the updated text. this.selectionEnd = this.selectionStart = before.length + replace.length; } e.stopPropagation(); //prevent bubbling (new-school) return false; //prevent bubbling (old-school) } }); }