Repeat Key

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل 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);
})();