Repeat Key

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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.

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

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

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

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

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

// ==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);
})();