SimpleShortcuts

Create single-key shortcuts for any page. Press ctrl+alt+s to open manager.

  1. // ==UserScript==
  2. // @name SimpleShortcuts
  3. // @namespace com.gmail.fujifruity.greasemonkey
  4. // @version 2.4
  5. // @description Create single-key shortcuts for any page. Press ctrl+alt+s to open manager.
  6. // @author fujifruity
  7. // @match *://*/*
  8. // @grant GM.getValue
  9. // @grant GM.setValue
  10. // @grant GM.listValues
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (async () => {
  15. 'use strict';
  16.  
  17. // With Tampermonkey, you can directly modify shortcuts from Storage tab (Advance mode only) in the editor.
  18.  
  19. // Find the longest matching url from GM cache
  20. let targetUrl; {
  21. const urls = await GM.listValues()
  22. console.log('urls', urls)
  23. const foundUrl = urls.sort((a, b) => b.length - a.length).find(url => location.href.startsWith(url))
  24. targetUrl = foundUrl ?? location.protocol + '//' + location.host + location.pathname
  25. }
  26. const getShortcuts = async () => {
  27. const shortcuts = await GM.getValue(targetUrl) ?? '{}'
  28. return JSON.parse(shortcuts)
  29. }
  30. const elem = selector => document.querySelector(selector)
  31. const setShortcuts = async () => {
  32. const shortcuts = await getShortcuts()
  33. Object.keys(shortcuts).forEach(k => {
  34. window.addEventListener('keydown', event => {
  35. if (["INPUT", "TEXTAREA"].includes(event.target.tagName) ||
  36. event.ctrlKey || event.altKey || event.metaKey || event.key != k) return
  37. const button = elem(shortcuts[k])
  38. button.focus()
  39. button.click()
  40. console.log('clicking', button);
  41. })
  42. })
  43. }
  44. // Set shortcuts if exist
  45. setShortcuts()
  46.  
  47. const modalId = 'fujifruity-simpleshortcuts'
  48. const modal = `
  49. <style>
  50. #${modalId} * { margin:4px; }
  51. #${modalId} #modalTitle { font-weight:bold; }
  52. #${modalId} {
  53. z-index:99999; width:auto; max-height:90%; position:fixed;
  54. margin:16px; padding:16px; border-radius:8px; overflow-y:auto;
  55. box-shadow:rgba(0, 0, 0, 0.35) 0px 5px 15px; background-color:white
  56. }
  57. </style>
  58. <div id=${modalId} >
  59. <div id=modalTitle>SimpleShortcuts for</div>
  60. <div id=targetUrl></div>
  61. <hr class="solid">
  62. <input id=keyInput size=3 placeholder=key maxlength=20>
  63. <input id=valueInput size=16 placeholder="CSS selector">
  64. <button id=saveButton>Save</button>
  65. <div id=shortcutList></div>
  66. </div> `
  67. const createModal = async () => {
  68. document.body.innerHTML = modal + document.body.innerHTML
  69. // Init url input
  70. elem('#targetUrl').textContent = targetUrl
  71. // Init key input
  72. const keyInput = elem('#keyInput')
  73. keyInput.addEventListener('keyup', event => {
  74. keyInput.value = event.key
  75. keyInput.size = event.key.length
  76. })
  77. // Init shortcut list
  78. const updateShortcutList = async () => {
  79. const shortcuts = await getShortcuts()
  80. const lines = Object.keys(shortcuts).map(k => `<div>${k ? k : '" "'} ${shortcuts[k]}</div>`).join('')
  81. elem('#shortcutList').innerHTML = lines
  82. }
  83. updateShortcutList()
  84. // Init save button
  85. elem('#saveButton').onclick = async () => {
  86. const key = elem('#keyInput').value
  87. const value = elem('#valueInput').value
  88. const shortcuts = await getShortcuts()
  89. if (!key) return
  90. if (value) {
  91. shortcuts[key] = value
  92. } else {
  93. delete shortcuts[key]
  94. }
  95. await GM.setValue(targetUrl, JSON.stringify(shortcuts))
  96. updateShortcutList()
  97. setShortcuts()
  98. }
  99. return elem('#' + modalId)
  100. }
  101.  
  102. window.addEventListener('keydown', async event => {
  103. if (!event.altKey && !event.metaKey || !event.ctrlKey || event.key != 's') return
  104. // Show modal
  105. const modal = elem('#' + modalId) ?? await createModal()
  106. modal.style.display = 'block'
  107. modal.focus()
  108. // Set event listeners to close modal
  109. const closeModal = modal => { modal.style.display = 'none' }
  110. const onKeydown = event => {
  111. if (["INPUT", "TEXTAREA"].includes(event.target.tagName) || event.key != 'Escape') return
  112. closeModal(modal)
  113. window.removeEventListener('keydown', onKeydown)
  114. }
  115. const onClick = event => {
  116. if (modal.contains(event.target)) return
  117. closeModal(modal)
  118. window.removeEventListener('click', onClick)
  119. }
  120. window.addEventListener('keydown', onKeydown)
  121. window.addEventListener('click', onClick)
  122. });
  123. })()