Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Manishearth/2583069 to your computer and use it in GitHub Desktop.
Save Manishearth/2583069 to your computer and use it in GitHub Desktop.
This is a userscript that adds shortcuts for adding <kbd> tags to posts. Designed for Stack Exchange sites.
// ==UserScript==
// @name _Add kbd shortcut
// @namespace StackExchange
// @description Adds a button and a keyboard shortcut (Alt-K) to add <kbd> tags.
// @match http://*.askubuntu.com/*
// @match http://*.onstartups.com/*
// @match http://*.serverfault.com/*
// @match http://*.stackapps.com/*
// @match http://*.stackexchange.com/*
// @match http://*.stackoverflow.com/*
// @match http://*.superuser.com/*
// ==/UserScript==
function AddKbdShortcuts ($) {
$("textarea.wmd-input").each (AddKbdButtonAsNeeded);
//.on() not working right!!
$("textarea.wmd-input").live ("focus", AddKbdButtonAsNeeded);
$("textarea.wmd-input").live ("keydown", InsertKbdTagByKeypress);
$("li.wmd-kbd-button") .live ("click", InsertKbdTagByClick);
/*------------*/
function AddKbdButtonAsNeeded () {
var jThis = $(this);
if ( ! jThis.data ("hasKbdBttn") ) {
//--- Find the button bar and add our button.
var btnBar = jThis.prevAll ("div.wmd-button-bar");
if (btnBar.length) {
//--- The button bar takes a while to AJAX-in.
var bbListTimer = setInterval ( function() {
var bbList = btnBar.find ("ul.wmd-button-row");
if (bbList.length) {
clearInterval (bbListTimer);
bbList.append (
'<li class="wmd-button wmd-kbd-button" '
+ 'title="Keyboard tag &lt;kbd&gt; Alt+K" '
+ 'style="left: 380px;">'
+ '<span style="background: white;">[kbd]</span></li>'
);
jThis.data ("hasKbdBttn", true);
}
},
100
);
}
}
}
function InsertKbdTagByKeypress (zEvent) {
//--- On Alt-K, insert the <kbd> set. Ignore all other keys.
if (zEvent.altKey && zEvent.which == 75) {
InsertKbdTag (this);
return false;
}
return true;
}
function InsertKbdTagByClick (zEvent) {
//--- From the clicked button, find the matching textarea.
var targArea = $(this).parents ("div.wmd-button-bar")
.nextAll ("textarea.wmd-input");
InsertKbdTag (targArea[0]);
targArea.focus ();
try {
//--- This is a utility function that SE currently provides on its pages.
StackExchange.MarkdownEditor.refreshAllPreviews ();
}
catch (e) {
console.warn ("***Userscript error: refreshAllPreviews() is no longer defined!");
}
}
function InsertKbdTag (node) {
//--- Wrap selected text or insert at curser.
var tagLength = 5; // Tag is: "<kbd>"
var oldText = node.value || node.textContent;
var newText;
var iTargetStart = node.selectionStart;
var iTargetEnd = node.selectionEnd;
var selectedText = oldText.slice (iTargetStart, iTargetEnd);
var possWrappedTxt;
try {
//--- Lazyman's overrun checking...
possWrappedTxt = oldText.slice (
iTargetStart - tagLength,
iTargetEnd + tagLength + 1
);
}
catch (e) {
possWrappedTxt = "Text can't be wrapped, cause we overran the string.";
}
/*--- Is the current selection wrapped? If so, just unwrap it.
This works the same way as SE's bold, italic, code, etc...
"]text[" --> "<kbd>]text[</kbd>"
"<kbd>]text[</kbd>" --> "]text["
"]<kbd>text</kbd>[" --> "<kbd>]<kbd>text</kbd>[</kbd>"
Except that:
"][" --> "<kbd>][</kbd>"
"<kbd>][</kbd>" --> "]["
with no placeholder text.
Note that `]` and `[` denote the selected text here.
*/
if (possWrappedTxt &&
selectedText == possWrappedTxt.replace (/^<kbd>((?:.|\n|\r)*)<\/kbd>$/, "$1")
) {
iTargetStart -= tagLength;
iTargetEnd += tagLength + 1;
newText = oldText.slice (0, iTargetStart)
+ selectedText + oldText.slice (iTargetEnd)
;
iTargetEnd = iTargetStart + selectedText.length;
}
else {
/*--- Here we will wrap the selection in our tags, but there is one extra
condition. We don't want to wrap leading or trailing whitespace.
*/
var trimSelctd = selectedText.match (/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/)
|| ["", "", "", ""]
;
if (trimSelctd.length != 4) {
console.warn ("***Userscript error: unexpected failure of whitespace RE.");
}
else {
newText = trimSelctd[1] //-- Leading whitespace, if any.
+ '<kbd>' + trimSelctd[2] + '</kbd>'
+ trimSelctd[3] //-- Trailing whitespace, if any.
;
newText = oldText.slice (0, iTargetStart)
+ newText + oldText.slice (iTargetEnd)
;
iTargetStart += tagLength + trimSelctd[1].length;
iTargetEnd += tagLength - trimSelctd[3].length;
}
}
//console.log (newText);
node.value = newText;
//--- After using spelling corrector, this gets buggered, hence the multiple sets.
node.textContent = newText;
//--- Have to reset the selection, since we overwrote the text.
node.selectionStart = iTargetStart;
node.selectionEnd = iTargetEnd;
}
}
withPages_jQuery (AddKbdShortcuts);
function withPages_jQuery (NAMED_FunctionToRun) {
//--- Use named functions for clarity and debugging...
var funcText = NAMED_FunctionToRun.toString ();
var funcName = funcText.replace (/^function\s+(\w+)\s*\((.|\n|\r)+$/, "$1");
var script = document.createElement ("script");
script.textContent = funcText + "\n\n";
script.textContent += 'jQuery(document).ready( function () {' + funcName + '(jQuery);} );';
document.body.appendChild (script);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment