MolView Keybindings

Add keybindings to molview.org

  1. // ==UserScript==
  2. // @name MolView Keybindings
  3. // @namespace https://shitchell.com/
  4. // @description Add keybindings to molview.org
  5. // @author Shaun Mitchell <shaun@shitchell.com>
  6. // @license wtfpl
  7. // @grant GM_addStyle
  8. // @match https://molview.org/*
  9. // @version 0.1
  10. // ==/UserScript==
  11.  
  12. // Send stuff to the console
  13. var DEBUG = true;
  14. var clickedToU = false;
  15. var observer = null;
  16.  
  17. function debug()
  18. {
  19. if (DEBUG)
  20. {
  21. let args = Array.from(arguments);
  22. args.unshift("[MolView KeyBindings]");
  23. console.log.apply(null, args);
  24. }
  25. }
  26.  
  27. // Binds a key to clicking an element with the given id
  28. function keyBindId(key, id)
  29. {
  30. document.body.addEventListener('keypress', function(ev) {
  31. if (ev.key === key && ev.target === document.body)
  32. {
  33. debug("got keypress event", ev, "matching keybinding for", document.getElementById(id));
  34. document.getElementById(id).click()
  35. }
  36. });
  37. }
  38.  
  39. function keyBindFunction(key, func)
  40. {
  41. document.body.addEventListener('keypress', function(ev) {
  42. if (ev.key === key)
  43. {
  44. debug("got keypress event", ev, "matching function", func);
  45. func();
  46. }
  47. });
  48. }
  49.  
  50. // Accepts the shortform of an element (e.g. C, S, Cl...)
  51. function setElement(element)
  52. {
  53. unsafeWindow.Sketcher.molpad.setTool("atom", {
  54. element: element
  55. });
  56. unsafeWindow.MolView.hideDialogs()
  57. }
  58.  
  59. // Creates a dialog to enter an element name
  60. function createElementDialog()
  61. {
  62. // Create the input element
  63. let input = document.createElement('input');
  64. input.id = 'element-input';
  65. input.placeholder = 'Element Symbol';
  66.  
  67. // Create the form
  68. let form = document.createElement('form');
  69. form.id = 'element-input-form';
  70. form.onsubmit = function(e) {
  71. e.preventDefault();
  72. e.stopPropagation();
  73. let elementShortName = document.getElementById('element-input').value;
  74. debug("input element", elementShortName);
  75. // Verify that this is a valid element
  76. if (elementShortName in unsafeWindow.ElementsMolarTable)
  77. {
  78. // Set the element
  79. setElement(elementShortName);
  80.  
  81. // Remove the element input form
  82. form.parentElement.removeChild(form);
  83. } else {
  84. debug("element not found", elementShortName);
  85. }
  86. }
  87.  
  88. // Text color reflects validity of element and escape exits
  89. input.addEventListener('keyup', function(ev) {
  90. // Get the current input text
  91. let elementShortName = document.getElementById('element-input').value;
  92.  
  93. if (ev.key === 'Escape')
  94. {
  95. // Remove the dialog box
  96. form.parentElement.removeChild(form);
  97. } else if (elementShortName === "") {
  98. this.classList.remove('invalid');
  99. } else {
  100. // Validate the element symbol
  101. if (elementShortName in unsafeWindow.ElementsMolarTable)
  102. {
  103. this.classList.remove('invalid');
  104. } else {
  105. this.classList.add('invalid');
  106. }
  107. }
  108. });
  109.  
  110. // Add the input box to the form
  111. form.appendChild(input);
  112.  
  113. // Add the form to the page
  114. document.body.appendChild(form);
  115.  
  116. // Focus the input box
  117. input.focus();
  118. }
  119.  
  120. // Watch for new elements (all of this mutation crap is strictly to click the initial "Close" button on the ToU
  121. function isElement(obj) {
  122. return obj instanceof Element || obj instanceof HTMLDocument;
  123. }
  124. function closeWelcome() {
  125. let closeBtn = document.querySelector("#welcome-button-bar .btn.close");
  126. debug("checking for closeBtn", closeBtn);
  127. if (isElement(closeBtn))
  128. {
  129. // Close the dialog
  130. debug("clicking close button and killing observer");
  131. unsafeWindow.MolView.hideDialogs();
  132.  
  133. // Remove the style to hide the dialog
  134. GM_addStyle(`#dialog-overlay {
  135. display: block;
  136. }`);
  137.  
  138. // Kill the observer
  139. observer.disconnect();
  140. observer = null;
  141. }
  142. }
  143.  
  144. (function()
  145. {
  146. 'use strict';
  147.  
  148. // Hide the agreement popup
  149. GM_addStyle(`#dialog-overlay {
  150. display: none;
  151. }`);
  152.  
  153. // Style the element input dialog
  154. GM_addStyle(`#element-input {
  155. position: fixed;
  156. width: 10em;
  157. height: 1.5em;
  158. z-index: 9001;
  159. bottom: 1em;
  160. left: 1em;
  161. border: none;
  162. background-image: none;
  163. background-color: transparent;
  164. -webkit-box-shadow: none;
  165. -moz-box-shadow: none;
  166. box-shadow: none;
  167. border-bottom: 1px solid black;
  168. background: rgba(255, 255, 255, 0.9);
  169. }`);
  170. GM_addStyle(`#element-input.invalid {
  171. color: red;
  172. }`);
  173. GM_addStyle(`#element-input::before {
  174. content: 'Element: ';
  175. }`);
  176.  
  177. // Click the agreement button when it loads
  178. observer = new MutationObserver(function(mutations) {
  179. closeWelcome();
  180. });
  181. observer.observe(document, {
  182. attributes: false,
  183. childList: true,
  184. characterData: false,
  185. subtree:true
  186. });
  187.  
  188. //// Tools
  189.  
  190. // m: drag tool
  191. keyBindId('m', 'action-mp-drag');
  192.  
  193. // c: clean structure
  194. keyBindId('c', 'action-mp-clean');
  195.  
  196. // e: eraser
  197. keyBindId('e', 'action-mp-eraser');
  198.  
  199. // u: update 3d model
  200. keyBindId('u', 'action-resolve');
  201.  
  202. // s: single bond
  203. keyBindId('s', 'action-mp-bond-single');
  204.  
  205. // d: double bond
  206. keyBindId('d', 'action-mp-bond-double');
  207.  
  208. // t: triple bond
  209. keyBindId('t', 'action-mp-bond-triple');
  210.  
  211. // =: add charge
  212. keyBindId('=', 'action-mp-charge-add');
  213.  
  214. // -: subtract charge
  215. keyBindId('-', 'action-mp-charge-sub');
  216.  
  217. //// Elements
  218.  
  219. // -: subtract charge
  220. keyBindId('C', 'action-mp-atom-c');
  221.  
  222. // -: subtract charge
  223. keyBindId('H', 'action-mp-atom-h');
  224.  
  225. // -: subtract charge
  226. keyBindId('N', 'action-mp-atom-n');
  227.  
  228. // -: subtract charge
  229. keyBindId('O', 'action-mp-atom-o');
  230.  
  231. // E: enter element
  232. keyBindFunction('E', createElementDialog);
  233. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址