Skip to content

Instantly share code, notes, and snippets.

@DanH42
Last active July 16, 2019 20:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanH42/ab83bf856abc7731bec0b0c35bef155d to your computer and use it in GitHub Desktop.
Save DanH42/ab83bf856abc7731bec0b0c35bef155d to your computer and use it in GitHub Desktop.
Override keyboard shortcuts in Gmail
// ==UserScript==
// @name Gmail Keyboard Shortcuts
// @namespace com.gmail.keyboard
// @version 1.0.4
// @description Override keyboard shortcuts in Gmail
// @author Dan Hlavenka
// @updateURL https://gist.githubusercontent.com/DanH42/ab83bf856abc7731bec0b0c35bef155d/raw
// @match https://mail.google.com/mail/*
// @grant none
// ==/UserScript==
// Custom shortcuts
var KEYBOARD_OVERRIDES = {
"sa": "*a", // Select all
"sn": "*n", // Select none
"sr": "*r", // Select read
"su": "*u", // Select unread
"s*": "*s", // Select starred
"st": "*t", // Select unstarred
"mr": "I", // Mark read
"mu": "U", // Mark unread
"g*": "gs", // Goto starred
"gs": "gt", // Goto Sent
"*": "s", // Star
"`c": "c", // Compose
"`/": "/", // Search
// Macros
"zs": "*u I *n gi u", // Select unread, mark read, select none, goto inbox, reload
// Unchanged shortcuts
"gi": "gi", // Goto inbox
"gd": "gd", // Goto Drafts
"ga": "ga", // Goto all mail
"u": "u", // Back to threadlist (refresh)
"j": "j", // Down
"k": "k", // Up
"x": "x", // Selext conversation
"e": "e", // Archive
"+": "+", // Mark important
"=": "+", // Mark important, but you didn't hold shift
"-": "-", // Mark unimportant
"?": "?", // Help (won't be accurate, but useful for editing shortcuts here)
};
// Time in ms to wait between simulated keypresses
var MACRO_DELAY = 15;
var KEY_EVENTS = ["keydown", "keypress", "keyup"];
(function(){
'use strict';
// Don't run in frames
if(window !== parent)
return;
var capsWarningTimeout = null;
// Check if the currently focused element is a textbox
var isActiveElementEditable = function(){
if(document.activeElement.isContentEditable)
return true;
if(["INPUT", "TEXTAREA", "IFRAME"].indexOf(document.activeElement.tagName) !== -1)
return true;
return false;
};
// Simulate a keypress
var pressKey = function(key){
// Space = pause
if(key === " ")
return;
var el = document.activeElement;
var keyCode = key.charCodeAt(0);
for(var i = 0; i < KEY_EVENTS.length; i++)
setTimeout(keyEvent.bind(this, el, KEY_EVENTS[i], key, keyCode), i * 5);
};
var keyEvent = function(el, type, key, keyCode){
var event = document.createEvent("Events");
if(event.initEvent)
event.initEvent(type, true, true);
event.keyCode = keyCode;
event.charCode = keyCode;
event.which = keyCode;
event.key = key;
if(key.toLowerCase() !== key)
event.shiftKey = true;
el.dispatchEvent(event);
};
// Keep track of multi-character shortcuts
var stack = "";
var stackTimeout = null;
var startStackTimeout = function(){
if(stackTimeout)
clearTimeout(stackTimeout);
stackTimeout = setTimeout(clearStack, 500);
};
var clearStack = function(){
if(stackTimeout){
clearTimeout(stackTimeout);
stackTimeout = null;
}
stack = "";
};
// Check if a keypress matches at least one viable override
var isPossibleMatch = function(key){
var search = stack + key;
for(var command in KEYBOARD_OVERRIDES){
if(command.startsWith(search))
return true;
}
return false;
};
var handleKeyEvent = function(e){
if(
// Don't respond to our own events
!e.isTrusted ||
// Don't capture keypresses that use modifier keys
e.ctrlKey || e.altKey || e.metaKey ||
// Don't capture keypresses in text boxes
isActiveElementEditable() ||
// Only handle single-character keys
e.key.length !== 1
)
return;
// Detect caps lock
if(e.key !== e.key.toLowerCase() && e.shiftKey === false)
showCapsWarning();
// Suppress keyup/keydown events without any additional logic
if(e.type !== "keypress"){
e.stopPropagation();
return;
}
// Only add a keypress to the stack if it's a potential match for a command
if(isPossibleMatch(e.key)){
stack += e.key;
if(KEYBOARD_OVERRIDES[stack]){
var action = KEYBOARD_OVERRIDES[stack];
console.log("MATCH", stack);
setTimeout(console.log.bind(this, "DONE"), action.length * MACRO_DELAY);
clearStack();
// Perform an action if we have a match
for(var i = 0; i < action.length; i++)
setTimeout(pressKey.bind(this, action[i]), i * MACRO_DELAY);
}else{
// If the keypress isn't an exact match (multi-key shortcuts), reset the timer
startStackTimeout();
}
}else{
// If the keypress won't result in a valid command, clear the stack
clearStack();
}
// Cancel the keypress
e.stopPropagation();
e.preventDefault();
return false;
};
var showCapsWarning = function(){
capsWarningTimeout && clearTimeout(capsWarningTimeout);
var oldCapsWarning = document.getElementById('capsWarning');
oldCapsWarning && oldCapsWarning.remove();
var capsWarning = document.createElement('div');
capsWarning.id = "capsWarning";
capsWarning.style.background = "rgba(0, 0, 0, .9)";
capsWarning.style.color = "white";
capsWarning.style.position = "fixed";
capsWarning.style.top = "50%";
capsWarning.style.left = "50%";
capsWarning.style.width = "200px";
capsWarning.style.height = "40px";
capsWarning.style.marginTop = "-30px";
capsWarning.style.marginLeft = "-110px";
capsWarning.style.fontSize = "32px";
capsWarning.style.textAlign = "center";
capsWarning.style.padding = "20px";
capsWarning.style.borderRadius = "10px";
capsWarning.style.opacity = "1";
capsWarning.style.transition = "opacity 2s";
capsWarning.innerText = "Caps Lock";
document.body.appendChild(capsWarning);
capsWarningTimeout = setTimeout(function(){
capsWarning.style.opacity = "0";
capsWarningTimeout = setTimeout(function(){
capsWarning && capsWarning.remove();
capsWarningTimeout = null;
}, 2100);
}, 100);
};
for(var i = 0; i < KEY_EVENTS.length; i++)
window.addEventListener(KEY_EVENTS[i], handleKeyEvent, true);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment