Repeat Key

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Repeat Key
// @author      to
// @namespace   https://github.com/to
// @version     0.2
// @match       https://twitter.com/*
// @description jホールドでリピート開始/リピート中にjで加速/他キーで停止
// @icon        https://www.google.com/s2/favicons?domain=twitter.com
// @license     MIT
// ==/UserScript==

(function() {
	const opt = {
		hold: 250,
		interval: 800,
		decrement: 200,
		min: 400,
		key: 'j',
	};
	// keypressイベントではcharCode(小文字)が使われる
	opt.charCode = opt.key.charCodeAt(0);
	opt.keyCode = opt.charCode - 32;

	var repeater = {
		onMouseDown: function name(params) {
			this.state.stop();
		},

		onKeyDown: function (e) {
			if (e.fake)
				return;

			// 対象外のキーか?
			if (e.keyCode != opt.keyCode) {
				this.state.stop();
				return;
			}

			if (e.repeat)
				repeater.cancelEvent(e);

			this.state.onKeyDown(e);
		},

		onKeyPress: function (e) {
			// 対象外のキーか?
			if (e.fake || e.keyCode != opt.charCode)
				return;

			this.state.onKeyPress(e);
		},

		onKeyUp: function (e) {
			// 対象外のキーか?
			if (e.fake || e.keyCode != opt.keyCode)
				return;

			this.state.onKeyUp(e);
		},

		startRepeat: function () {
			// 既存のリピートを停止する
			this.stopRepeat();
			this.timer = setInterval(() => {
				repeater.dispatchEvent();
			}, repeater.interval);
		},

		dispatchEvent: function () {
			document.dispatchEvent(repeater.createEvent('keydown'));
			document.dispatchEvent(repeater.createEvent('keypress'));
			document.dispatchEvent(repeater.createEvent('keyup'));
		},

		createEvent: function (type) {
			let event = new KeyboardEvent(type, {
				keyCode: type == 'keypress' ? opt.charCode : opt.keyCode,
				key: opt.key,
				bubbles: true,
			});

			// 生成したイベントにフラグを付加し、判別に用いる
			event.fake = true;
			return event;
		},

		stopRepeat: function () {
			clearInterval(this.timer);
		},

		cancelEvent: function (event) {
			// 他ハンドラも含めて伝播を完全に停止する
			event.preventDefault();
			event.stopPropagation();
			event.stopImmediatePropagation();
		},

		states: {
			normal: {
				onKeyDown: function (e) {
					// ホールド開始時刻を保存する
					repeater.start = Date.now();

					repeater.state = repeater.states.holding;
				},
				onKeyPress: function (e) { },
				onKeyUp: function (e) { },
				stop: function () { },
			},

			holding: {
				onKeyDown: function (e) {
					// ホールド開始から一定時間が経過していないか?
					if (Date.now() < (repeater.start + opt.hold))
						return;

					// リピート開始前にkeyupを発生させ、一連のキー押下を終わらせる
					document.dispatchEvent(repeater.createEvent('keyup'));

					repeater.interval = opt.interval;
					repeater.startRepeat();

					repeater.state = repeater.states.repeating;
				},
				onKeyPress: function e() { },
				onKeyUp: function (e) {
					repeater.state = repeater.states.normal;
				},
				stop: function () { },
			},

			repeating: {
				onKeyDown: function (e) {
					if (e.repeat)
						return;

					repeater.cancelEvent(e);

					// リピート間隔を減らす
					repeater.interval = Math.max(repeater.interval - opt.decrement, opt.min);
					repeater.startRepeat();
				},
				onKeyPress: function (e) {
					repeater.cancelEvent(e);
				},
				onKeyUp: function (e) {
					repeater.cancelEvent(e);
				},
				stop: function () {
					repeater.stopRepeat();

					repeater.state = repeater.states.normal;
				},
			},
		}
	};
	repeater.state = repeater.states.normal;

	document.addEventListener('keydown', repeater.onKeyDown.bind(repeater), true);
	document.addEventListener('keypress', repeater.onKeyPress.bind(repeater), true);
	document.addEventListener('keyup', repeater.onKeyUp.bind(repeater), true);
	document.addEventListener('mousedown', repeater.onMouseDown.bind(repeater), true);
})();