// ==UserScript==
// @name Surrounded
// @namespace cpriest
// @version 0.3
// @description Surrounds selected text on web pages with pairs of characters when typed, keeps selection unchanged.
// @author Clint Priest
// @homepage https://github.com/cpriest/userscripts/tree/master/surrounded
// @include *
// @grant none
// @license MIT
// @compatible firefox
// @compatible chrome
// @compatible opera
// @compatible safari
// @todo Make it work with code editors (CodeMirror, ace)
// ==/UserScript==
(function() {
'use strict';
const CTRL = 1,
ALT = 2,
SHIFT = 4;
const ActiveKeys = [`'`, `"`, '(', ')', '{', '}', '[', ']', '`', '*', '_', '<', '>',];
/**
* Returns true if we work with the passed in elem
*
* @param {Element} elem
*
* @returns boolean
*/
function WeWorkWithThisElement(elem) {
if(elem.tagName == 'TEXTAREA')
return true;
if(elem.tagName == 'INPUT') {
if('selectionStart' in elem || 'selectionEnd' in elem)
return true;
}
return false;
}
function note(...args) {
console.log(...args);
}
/**
* Surrounds the given elements selection with the character set
*
* @param {HTMLInputElement|HTMLTextAreaElement} elem
* @param key
*/
function Surround(elem, key) {
let { selectionStart, selectionEnd, selectionDirection } = elem;
let leftKey = key + '',
rightKey = key + '',
leftValue = elem.value.substring(0, elem.selectionStart),
value = elem.value.substring(elem.selectionStart, elem.selectionEnd),
rightValue = elem.value.substring(elem.selectionEnd);
switch(leftKey) {
case '{':
rightKey = '}';
break;
case '}':
rightKey = '{';
[leftKey, rightKey] = [rightKey, leftKey];
break;
case '(':
rightKey = ')';
break;
case ')':
rightKey = '(';
[leftKey, rightKey] = [rightKey, leftKey];
break;
case '[':
rightKey = ']';
break;
case ']':
rightKey = '[';
[leftKey, rightKey] = [rightKey, leftKey];
break;
case '<':
rightKey = '>';
break;
case '>':
rightKey = '<';
[leftKey, rightKey] = [rightKey, leftKey];
break;
}
// Remove Mode
if(leftValue.substr(-1) === leftKey && rightValue.substr(0, 1) === rightKey) {
leftValue = leftValue.substr(0, leftValue.length - 1);
rightValue = rightValue.substr(1);
leftKey = rightKey = '';
selectionStart--;
selectionEnd--;
} else {
// Add Mode
selectionStart++;
selectionEnd++;
}
elem.value = `${leftValue}${leftKey}${value}${rightKey}${rightValue}`;
elem.selectionStart = selectionStart;
elem.selectionEnd = selectionEnd;
}
window.document.addEventListener('keydown',
/** @param {KeyboardEvent} e */e => {
e.mods = (e.ctrlKey) + (e.altKey << 1) + (e.shiftKey << 2);
if(e.mods > 0 && e.mods !== 4)
return; // note(`${e.key}: CTRL/ALT active e.mods=${e.mods}`);
if(ActiveKeys.indexOf(e.key) == -1)
return; // note(`${e.key}: not a key we care about`);
if(!document.activeElement)
return; // note(`${e.key}: no valid activeElement: %o`, document.activeElement);
let ae = document.activeElement;
if(!WeWorkWithThisElement(ae))
return; // note(`${e.key}: We don't work with: %o`, ae);
if(ae.selectionStart === ae.selectionEnd)
return; // note(`${e.key}: No characters are selected, start=${ae.selectionStart}, end=${ae.selectionEnd}` );
Surround(ae, e.key);
e.preventDefault();
e.stopPropagation();
}, true);
})();