Surrounded

Surrounds selected text on web pages with pairs of characters when typed, keeps selection unchanged.

目前为 2019-07-11 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Surrounded
  3. // @namespace cpriest
  4. // @version 0.3
  5. // @description Surrounds selected text on web pages with pairs of characters when typed, keeps selection unchanged.
  6. // @author Clint Priest
  7. // @homepage https://github.com/cpriest/userscripts/tree/master/surrounded
  8. // @include *
  9. // @grant none
  10. // @license MIT
  11. // @compatible firefox
  12. // @compatible chrome
  13. // @compatible opera
  14. // @compatible safari
  15. // @todo Make it work with code editors (CodeMirror, ace)
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. const CTRL = 1,
  22. ALT = 2,
  23. SHIFT = 4;
  24.  
  25. const ActiveKeys = [`'`, `"`, '(', ')', '{', '}', '[', ']', '`', '*', '_', '<', '>',];
  26.  
  27. /**
  28. * Returns true if we work with the passed in elem
  29. *
  30. * @param {Element} elem
  31. *
  32. * @returns boolean
  33. */
  34. function WeWorkWithThisElement(elem) {
  35. if(elem.tagName == 'TEXTAREA')
  36. return true;
  37.  
  38. if(elem.tagName == 'INPUT') {
  39. if('selectionStart' in elem || 'selectionEnd' in elem)
  40. return true;
  41. }
  42. return false;
  43. }
  44.  
  45. function note(...args) {
  46. console.log(...args);
  47. }
  48.  
  49. /**
  50. * Surrounds the given elements selection with the character set
  51. *
  52. * @param {HTMLInputElement|HTMLTextAreaElement} elem
  53. * @param key
  54. */
  55. function Surround(elem, key) {
  56. let { selectionStart, selectionEnd, selectionDirection } = elem;
  57.  
  58. let leftKey = key + '',
  59. rightKey = key + '',
  60. leftValue = elem.value.substring(0, elem.selectionStart),
  61. value = elem.value.substring(elem.selectionStart, elem.selectionEnd),
  62. rightValue = elem.value.substring(elem.selectionEnd);
  63.  
  64. switch(leftKey) {
  65. case '{':
  66. rightKey = '}';
  67. break;
  68. case '}':
  69. rightKey = '{';
  70. [leftKey, rightKey] = [rightKey, leftKey];
  71. break;
  72. case '(':
  73. rightKey = ')';
  74. break;
  75. case ')':
  76. rightKey = '(';
  77. [leftKey, rightKey] = [rightKey, leftKey];
  78. break;
  79. case '[':
  80. rightKey = ']';
  81. break;
  82. case ']':
  83. rightKey = '[';
  84. [leftKey, rightKey] = [rightKey, leftKey];
  85. break;
  86. case '<':
  87. rightKey = '>';
  88. break;
  89. case '>':
  90. rightKey = '<';
  91. [leftKey, rightKey] = [rightKey, leftKey];
  92. break;
  93. }
  94.  
  95. // Remove Mode
  96. if(leftValue.substr(-1) === leftKey && rightValue.substr(0, 1) === rightKey) {
  97. leftValue = leftValue.substr(0, leftValue.length - 1);
  98. rightValue = rightValue.substr(1);
  99. leftKey = rightKey = '';
  100. selectionStart--;
  101. selectionEnd--;
  102. } else {
  103. // Add Mode
  104. selectionStart++;
  105. selectionEnd++;
  106. }
  107.  
  108. elem.value = `${leftValue}${leftKey}${value}${rightKey}${rightValue}`;
  109. elem.selectionStart = selectionStart;
  110. elem.selectionEnd = selectionEnd;
  111. }
  112.  
  113. window.document.addEventListener('keydown',
  114. /** @param {KeyboardEvent} e */e => {
  115. e.mods = (e.ctrlKey) + (e.altKey << 1) + (e.shiftKey << 2);
  116.  
  117. if(e.mods > 0 && e.mods !== 4)
  118. return; // note(`${e.key}: CTRL/ALT active e.mods=${e.mods}`);
  119.  
  120. if(ActiveKeys.indexOf(e.key) == -1)
  121. return; // note(`${e.key}: not a key we care about`);
  122.  
  123. if(!document.activeElement)
  124. return; // note(`${e.key}: no valid activeElement: %o`, document.activeElement);
  125.  
  126. let ae = document.activeElement;
  127. if(!WeWorkWithThisElement(ae))
  128. return; // note(`${e.key}: We don't work with: %o`, ae);
  129.  
  130. if(ae.selectionStart === ae.selectionEnd)
  131. return; // note(`${e.key}: No characters are selected, start=${ae.selectionStart}, end=${ae.selectionEnd}` );
  132.  
  133. Surround(ae, e.key);
  134.  
  135. e.preventDefault();
  136. e.stopPropagation();
  137.  
  138. }, true);
  139. })();