星号密码显示助手

当鼠标停留在密码框时显示星号密码。再也不担心忘记密码和输错密码了。

  1. // ==UserScript==
  2. // @name 星号密码显示助手
  3. // @namespace https://github.com/syhyz1990/starpassword
  4. // @version 1.0.9
  5. // @author YouXiaoHou
  6. // @description 当鼠标停留在密码框时显示星号密码。再也不担心忘记密码和输错密码了。
  7. // @match *://*/*
  8. // @license MIT
  9. // @homepage https://www.youxiaohou.com/tool/install-starpassword.html
  10. // @supportURL https://github.com/syhyz1990/starpassword
  11. // @require https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.js
  12. // @resource swalStyle https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.css
  13. // @run-at document-start
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_getResourceText
  18. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjkgNTEuMkg0Ni4xYy0xLjIgMC0yLjEtLjktMi4xLTIuMVYyOS44YzAtNy4xIDguOS0xNC45IDIwLTE0LjkgMTEgMCAyMCA3LjggMjAgMTQuOXY2LjRjLjYgMy42IDMuOCA2LjQgNy43IDYuNHM3LjItMi44IDcuNy02LjRoLjF2LTYuNEM5OS41IDEzLjQgODMuNiAwIDY0IDBTMjguNSAxMy40IDI4LjUgMjkuOFY0OWMwIDEuMi0uOSAyLjEtMi4xIDIuMUgyNGMtNy40IDAtMTMuMyA1LjctMTMuMyAxMi44djUxLjJjMCA3LjEgNiAxMi44IDEzLjMgMTIuOGg4MGM3LjQgMCAxMy4zLTUuNyAxMy4zLTEyLjh2LTUxYy0uMS03LjEtNi4xLTEyLjktMTMuNC0xMi45eiIgZmlsbD0iIzQ0NCIvPjxwYXRoIGQ9Ik02Ni44IDY2LjRsNCAxMi40Yy40IDEuMiAxLjUgMiAyLjggMmgxM2MyLjkgMCA0LjEgMy43IDEuNyA1LjRsLTEwLjUgNy42Yy0xIC44LTEuNSAyLjEtMS4xIDMuM2w0IDEyLjRjLjkgMi43LTIuMiA1LTQuNiAzLjNsLTEwLjUtNy42Yy0xLS44LTIuNC0uOC0zLjUgMGwtMTAuNSA3LjZjLTIuMyAxLjctNS41LS42LTQuNi0zLjNsNC0xMi40Yy40LTEuMiAwLTIuNi0xLjEtMy4zbC0xMC41LTcuNmMtMi4zLTEuNy0xLjEtNS40IDEuNy01LjRoMTNjMS4zIDAgMi40LS44IDIuOC0ybDQtMTIuNGMxLjItMi43IDUtMi43IDUuOSAweiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==
  19. // ==/UserScript==
  20.  
  21. (function () {
  22. 'use strict';
  23.  
  24. let MutationObserverNew = null;
  25.  
  26. let util = {
  27. getValue(name) {
  28. return GM_getValue(name);
  29. },
  30.  
  31. setValue(name, value) {
  32. GM_setValue(name, value);
  33. },
  34.  
  35. addStyle(id, tag, css) {
  36. tag = tag || 'style';
  37. let doc = document, styleDom = doc.getElementById(id);
  38. if (styleDom) return;
  39. let style = doc.createElement(tag);
  40. style.rel = 'stylesheet';
  41. style.id = id;
  42. tag === 'style' ? style.innerHTML = css : style.href = css;
  43. document.head.appendChild(style);
  44. },
  45. };
  46.  
  47. let main = {
  48. /**
  49. * 配置默认值
  50. */
  51. initValue() {
  52. let value = [{
  53. name: 'setting_wait_time',
  54. value: 300
  55. }, {
  56. name: 'setting_show_method',
  57. value: 0
  58. }];
  59.  
  60. value.forEach((v) => {
  61. util.getValue(v.name) === undefined && util.setValue(v.name, v.value);
  62. });
  63. },
  64.  
  65. /**
  66. * 保存原始的MutationObserver,防止被覆盖
  67. */
  68. observer() {
  69. MutationObserverNew = window.MutationObserver;
  70. },
  71.  
  72. registerMenuCommand() {
  73. GM_registerMenuCommand('⚙️ 设置', () => {
  74. let html = `<div style="font-size: 1em;">
  75. <label class="starpassword-setting-label">显示密码方式
  76. <select id="S-starpassword-show-method" class="starpassword-select">
  77. <option value="0" ${util.getValue('setting_show_method') == 0 ? 'selected' : ''}>鼠标悬浮在密码框上时</option>
  78. <option value="1" ${util.getValue('setting_show_method') == 1 ? 'selected' : ''}>双击密码框时</option>
  79. <option value="2" ${util.getValue('setting_show_method') == 2 ? 'selected' : ''}>单击密码框时</option>
  80. <option value="3" ${util.getValue('setting_show_method') == 3 ? 'selected' : ''}>按下Ctrl并单击密码框时</option>
  81. </select>
  82. </label>
  83. <label class="starpassword-setting-label"><span>等待时间 <span id="S-starpassword-wait-time-label">(${util.getValue('setting_wait_time')}毫秒)</span></span><input type="range" id="S-starpassword-wait-time" min="0" max="1000" step="50" value="${util.getValue('setting_wait_time')}" style="width: 180px;"></label>
  84. </div>`;
  85. Swal.fire({
  86. title: '星号密码显示助手',
  87. html,
  88. icon: 'info',
  89. showCloseButton: true,
  90. confirmButtonText: '保存',
  91. footer: '<div style="text-align: center;font-size: 1em;">Powered by <a href="https://www.youxiaohou.com">油小猴</a></div>',
  92. customClass: {
  93. container: 'starpassword-container',
  94. popup: 'starpassword-popup'
  95. }
  96. }).then((res) => {
  97. res.isConfirmed && history.go(0);
  98. });
  99.  
  100. document.getElementById('S-starpassword-show-method').addEventListener('change', (e) => {
  101. util.setValue('setting_show_method', e.currentTarget.value);
  102. });
  103. document.getElementById('S-starpassword-wait-time').addEventListener('change', (e) => {
  104. util.setValue('setting_wait_time', e.target.value);
  105. document.getElementById('S-starpassword-wait-time-label').innerText = `(${e.target.value}毫秒)`;
  106. });
  107. });
  108. },
  109.  
  110. addPluginStyle() {
  111. let style = `
  112. .starpassword-container { z-index: 999999!important }
  113. .starpassword-popup { font-size: 14px!important }
  114. .starpassword-setting-label { display:flex; align-items: center; justify-content: space-between; padding-top: 18px; }
  115. .starpassword-select { background: #f3fcff; height: 28px; width: 180px; line-height: 28px; border: 1px solid #9bc0dd; border-radius: 2px;}
  116. `;
  117.  
  118. if (document.head) {
  119. util.addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle'));
  120. util.addStyle('starpassword-style', 'style', style);
  121. }
  122.  
  123. const headObserver = new MutationObserver(() => {
  124. util.addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle'));
  125. util.addStyle('starpassword-style', 'style', style);
  126. });
  127. headObserver.observe(document.head, {childList: true, subtree: true});
  128. },
  129.  
  130. isTopWindow() {
  131. return window.self === window.top;
  132. },
  133.  
  134. showPassword() {
  135. const KEY_ENTER = 13;
  136. const KEY_CTRL = 17;
  137. let behave = util.getValue('setting_show_method');
  138. let wait = util.getValue('setting_wait_time');
  139.  
  140. function mouseOver(tar) {
  141. tar.addEventListener('mouseover', () => {
  142. tar.isMouseOver = true;
  143. setTimeout(() => {
  144. if (tar.isMouseOver) {
  145. tar.type = 'text';
  146. }
  147. }, wait);
  148. }, false);
  149.  
  150. tar.addEventListener('mouseout', () => {
  151. tar.isMouseOver = false;
  152. tar.type = 'password';
  153. }, false);
  154.  
  155. tar.addEventListener('blur', () => {
  156. tar.type = 'password';
  157. }, false);
  158.  
  159. tar.addEventListener('keydown', e => {
  160. if (e.keyCode === KEY_ENTER) {
  161. tar.type = 'password';
  162. }
  163. }, false);
  164. }
  165.  
  166. function mouseDblClick(tar) {
  167. tar.addEventListener('dblclick', () => {
  168. tar.type = tar.type === 'password' ? 'text' : 'password';
  169. }, false);
  170.  
  171. tar.addEventListener('blur', () => {
  172. tar.type = 'password';
  173. }, false);
  174.  
  175. tar.addEventListener('keydown', e => {
  176. if (e.keyCode === KEY_ENTER) {
  177. tar.type = 'password';
  178. }
  179. }, false);
  180. }
  181.  
  182. function mouseFocus(tar) {
  183. tar.addEventListener('focus', () => {
  184. tar.type = 'text';
  185. }, false);
  186.  
  187. tar.addEventListener('blur', () => {
  188. tar.type = 'password';
  189. }, false);
  190.  
  191. tar.addEventListener('keydown', e => {
  192. if (e.keyCode === KEY_ENTER) {
  193. tar.type = 'password';
  194. }
  195. }, false);
  196. }
  197.  
  198. function ctrlKeyShift(tar) {
  199. let isHide = true;
  200. let notPressCtrl = true;
  201. let onlyCtrl = true;
  202.  
  203. tar.addEventListener('blur', () => {
  204. tar.type = 'password';
  205. isHide = true;
  206. notPressCtrl = true;
  207. onlyCtrl = true;
  208. }, false);
  209.  
  210. tar.addEventListener('keyup', e => {
  211. if (e.keyCode === KEY_CTRL) {
  212. if (onlyCtrl) {
  213. isHide = !isHide;
  214. } else {
  215. isHide = false;
  216. }
  217.  
  218. if (isHide) {
  219. tar.type = 'password';
  220. } else {
  221. tar.type = 'text';
  222. }
  223. notPressCtrl = true;
  224. onlyCtrl = true;
  225. }
  226. }, false);
  227.  
  228. tar.addEventListener('keydown', e => {
  229. if (e.keyCode === KEY_ENTER) {
  230. tar.type = 'password';
  231. isHide = true;
  232. notPressCtrl = true;
  233. onlyCtrl = true;
  234. } else if (e.keyCode === KEY_CTRL) {
  235. if (notPressCtrl) {
  236. tar.type = 'text';
  237. notPressCtrl = false;
  238. onlyCtrl = true;
  239. }
  240. } else {
  241. onlyCtrl = notPressCtrl;
  242. }
  243. }, false);
  244. }
  245.  
  246. const actionsArr = [mouseOver, mouseDblClick, mouseFocus, ctrlKeyShift];
  247. const doc = window.document;
  248. const modified = new WeakSet();
  249.  
  250. function modifyAllInputs() {
  251. const passwordInputs = doc.querySelectorAll('input[type=password]');
  252. passwordInputs.forEach(input => {
  253. if (!modified.has(input)) {
  254. actionsArr[behave](input);
  255. modified.add(input);
  256. }
  257. });
  258. }
  259.  
  260. function modifyWeb() {
  261. modifyAllInputs();
  262. }
  263.  
  264. modifyWeb();
  265.  
  266. const docObserver = new MutationObserverNew(() => {
  267. // NOTE: Despite we can recursively check element from addNodes.
  268. // Benchmark shows that it is much fast to just use `querySelectorAll` to find password inputs
  269. modifyWeb();
  270. });
  271.  
  272. docObserver.observe(doc.documentElement, {
  273. childList: true,
  274. subtree: true,
  275. // Some website add input with text type at first, then change its type to password.
  276. attributes: true,
  277. attributeFilter: ['type']
  278. });
  279. },
  280.  
  281. init() {
  282. this.observer();
  283. this.initValue();
  284. this.showPassword();
  285. this.addPluginStyle();
  286. this.isTopWindow() && this.registerMenuCommand();
  287. }
  288. };
  289. main.init();
  290. })();

QingJ © 2025

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