Repeat Key

jホールドでリピート開始/リピート中にjで加速/他キーで停止

目前為 2021-05-21 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Repeat Key
  3. // @author to
  4. // @namespace https://github.com/to
  5. // @version 0.1
  6. // @match https://twitter.com/*
  7. // @description jホールドでリピート開始/リピート中にjで加速/他キーで停止
  8. // @icon https://www.google.com/s2/favicons?domain=twitter.com
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. const opt = {
  14. hold: 250,
  15. interval: 1000,
  16. decrement: 200,
  17. min: 400,
  18. key: 'j',
  19. };
  20. // keypressイベントではcharCode(小文字)が使われる
  21. opt.charCode = opt.key.charCodeAt(0);
  22. opt.keyCode = opt.charCode - 32;
  23.  
  24. var repeater = {
  25. onKeyDown: function (e) {
  26. if (e.fake)
  27. return;
  28.  
  29. // 対象外のキーか?
  30. if (e.keyCode != opt.keyCode) {
  31. this.state.stop();
  32. return;
  33. }
  34.  
  35. if (e.repeat)
  36. repeater.cancelEvent(e);
  37.  
  38. this.state.onKeyDown(e);
  39. },
  40.  
  41. onKeyPress: function (e) {
  42. // 対象外のキーか?
  43. if (e.fake || e.keyCode != opt.charCode)
  44. return;
  45.  
  46. this.state.onKeyPress(e);
  47. },
  48.  
  49. onKeyUp: function (e) {
  50. // 対象外のキーか?
  51. if (e.fake || e.keyCode != opt.keyCode)
  52. return;
  53.  
  54. this.state.onKeyUp(e);
  55. },
  56.  
  57. startRepeat: function () {
  58. // 既存のリピートを停止する
  59. this.stopRepeat();
  60. this.timer = setInterval(() => {
  61. repeater.dispatchEvent();
  62. }, repeater.interval);
  63. },
  64.  
  65. dispatchEvent: function () {
  66. document.dispatchEvent(repeater.createEvent('keydown'));
  67. document.dispatchEvent(repeater.createEvent('keypress'));
  68. document.dispatchEvent(repeater.createEvent('keyup'));
  69. },
  70.  
  71. createEvent: function (type) {
  72. let event = new KeyboardEvent(type, {
  73. keyCode: type == 'keypress' ? opt.charCode : opt.keyCode,
  74. key: opt.key,
  75. bubbles: true,
  76. });
  77.  
  78. // 生成したイベントにフラグを付加し、判別に用いる
  79. event.fake = true;
  80. return event;
  81. },
  82.  
  83. stopRepeat: function () {
  84. clearInterval(this.timer);
  85. },
  86.  
  87. cancelEvent: function (event) {
  88. // 他ハンドラも含めて伝播を完全に停止する
  89. event.preventDefault();
  90. event.stopPropagation();
  91. event.stopImmediatePropagation();
  92. },
  93.  
  94. states: {
  95. normal: {
  96. onKeyDown: function (e) {
  97. // ホールド開始時刻を保存する
  98. repeater.start = Date.now();
  99.  
  100. repeater.state = repeater.states.holding;
  101. },
  102. onKeyPress: function (e) { },
  103. onKeyUp: function (e) { },
  104. stop: function () { },
  105. },
  106.  
  107. holding: {
  108. onKeyDown: function (e) {
  109. // ホールド開始から一定時間が経過していないか?
  110. if (Date.now() < (repeater.start + opt.hold))
  111. return;
  112.  
  113. // リピート開始前にkeyupを発生させ、一連のキー押下を終わらせる
  114. document.dispatchEvent(repeater.createEvent('keyup'));
  115.  
  116. repeater.interval = opt.interval;
  117. repeater.startRepeat();
  118.  
  119. repeater.state = repeater.states.repeating;
  120. },
  121. onKeyPress: function e() { },
  122. onKeyUp: function (e) {
  123. repeater.state = repeater.states.normal;
  124. },
  125. stop: function () { },
  126. },
  127.  
  128. repeating: {
  129. onKeyDown: function (e) {
  130. if (e.repeat)
  131. return;
  132.  
  133. repeater.cancelEvent(e);
  134.  
  135. // リピート間隔を減らす
  136. repeater.interval = Math.max(repeater.interval - opt.decrement, opt.min);
  137. repeater.startRepeat();
  138. },
  139. onKeyPress: function (e) {
  140. repeater.cancelEvent(e);
  141. },
  142. onKeyUp: function (e) {
  143. repeater.cancelEvent(e);
  144. },
  145. stop: function () {
  146. repeater.stopRepeat();
  147.  
  148. repeater.state = repeater.states.normal;
  149. },
  150. },
  151. }
  152. };
  153. repeater.state = repeater.states.normal;
  154.  
  155. document.addEventListener('keydown', repeater.onKeyDown.bind(repeater), true);
  156. document.addEventListener('keypress', repeater.onKeyPress.bind(repeater), true);
  157. document.addEventListener('keyup', repeater.onKeyUp.bind(repeater), true);
  158. })();