// ==UserScript== // @name 星号密码显示助手 // @namespace https://github.com/syhyz1990/starpassword // @version 1.1.0 // @author YouXiaoHou // @description 当你网站点击密码框时显示隐藏的密码,妈妈再也不用担心输错密码了。 // @match *://*/* // @license 小害怕 // @homepage https://www.youxiaohou.com/tool/install-starpassword.html // @supportURL https://github.com/syhyz1990/starpassword // @require https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.js // @resource swalStyle https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.css // @run-at document-start // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_getResourceText // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjkgNTEuMkg0Ni4xYy0xLjIgMC0yLjEtLjktMi4xLTIuMVYyOS44YzAtNy4xIDguOS0xNC45IDIwLTE0LjkgMTEgMCAyMCA3LjggMjAgMTQuOXY2LjRjLjYgMy42IDMuOCA2LjQgNy43IDYuNHM3LjItMi44IDcuNy02LjRoLjF2LTYuNEM5OS41IDEzLjQgODMuNiAwIDY0IDBTMjguNSAxMy40IDI4LjUgMjkuOFY0OWMwIDEuMi0uOSAyLjEtMi4xIDIuMUgyNGMtNy40IDAtMTMuMyA1LjctMTMuMyAxMi44djUxLjJjMCA3LjEgNiAxMi44IDEzLjMgMTIuOGg4MGM3LjQgMCAxMy4zLTUuNyAxMy4zLTEyLjh2LTUxYy0uMS03LjEtNi4xLTEyLjktMTMuNC0xMi45eiIgZmlsbD0iIzQ0NCIvPjxwYXRoIGQ9Ik02Ni44IDY2LjRsNCAxMi40Yy40IDEuMiAxLjUgMiAyLjggMmgxM2MyLjkgMCA0LjEgMy43IDEuNyA1LjRsLTEwLjUgNy42Yy0xIC44LTEuNSAyLjEtMS4xIDMuM2w0IDEyLjRjLjkgMi43LTIuMiA1LTQuNiAzLjNsLTEwLjUtNy42Yy0xLS44LTIuNC0uOC0zLjUgMGwtMTAuNSA3LjZjLTIuMyAxLjctNS41LS42LTQuNi0zLjNsNC0xMi40Yy40LTEuMiAwLTIuNi0xLjEtMy4zbC0xMC41LTcuNmMtMi4zLTEuNy0xLjEtNS40IDEuNy01LjRoMTNjMS4zIDAgMi40LS44IDIuOC0ybDQtMTIuNGMxLjItMi43IDUtMi43IDUuOSAweiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg== // @downloadURL https://update.greasyfork.icu/scripts/517481/%E6%98%9F%E5%8F%B7%E5%AF%86%E7%A0%81%E6%98%BE%E7%A4%BA%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/517481/%E6%98%9F%E5%8F%B7%E5%AF%86%E7%A0%81%E6%98%BE%E7%A4%BA%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function () { 'use strict'; let MutationObserverNew = null; let util = { getValue(name) { return GM_getValue(name); }, setValue(name, value) { GM_setValue(name, value); }, addStyle(id, tag, css) { tag = tag || 'style'; let doc = document, styleDom = doc.getElementById(id); if (styleDom) return; let style = doc.createElement(tag); style.rel = 'stylesheet'; style.id = id; tag === 'style' ? style.innerHTML = css : style.href = css; document.head.appendChild(style); }, }; let main = { /** * 配置默认值 */ initValue() { let value = [{ name: 'setting_wait_time', value: 300 }, { name: 'setting_show_method', value: 0 }]; value.forEach((v) => { util.getValue(v.name) === undefined && util.setValue(v.name, v.value); }); }, /** * 保存原始的MutationObserver,防止被覆盖 */ observer() { MutationObserverNew = window.MutationObserver; }, registerMenuCommand() { GM_registerMenuCommand('⚙️ 设置', () => { let html = `
`; Swal.fire({ title: '星号密码显示助手', html, icon: 'info', showCloseButton: true, confirmButtonText: '保存', footer: '
Powered by 油小猴
', customClass: { container: 'starpassword-container', popup: 'starpassword-popup' } }).then((res) => { res.isConfirmed && history.go(0); }); document.getElementById('S-starpassword-show-method').addEventListener('change', (e) => { util.setValue('setting_show_method', e.currentTarget.value); }); document.getElementById('S-starpassword-wait-time').addEventListener('change', (e) => { util.setValue('setting_wait_time', e.target.value); document.getElementById('S-starpassword-wait-time-label').innerText = `(${e.target.value}毫秒)`; }); }); }, addPluginStyle() { let style = ` .starpassword-container { z-index: 999999!important } .starpassword-popup { font-size: 14px!important } .starpassword-setting-label { display:flex; align-items: center; justify-content: space-between; padding-top: 18px; } .starpassword-select { background: #f3fcff; height: 28px; width: 180px; line-height: 28px; border: 1px solid #9bc0dd; border-radius: 2px;} `; if (document.head) { util.addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle')); util.addStyle('starpassword-style', 'style', style); } const headObserver = new MutationObserver(() => { util.addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle')); util.addStyle('starpassword-style', 'style', style); }); headObserver.observe(document.head, {childList: true, subtree: true}); }, isTopWindow() { return window.self === window.top; }, showPassword() { const KEY_ENTER = 13; const KEY_CTRL = 17; let behave = util.getValue('setting_show_method'); let wait = util.getValue('setting_wait_time'); function mouseOver(tar) { tar.addEventListener('mouseover', () => { tar.isMouseOver = true; setTimeout(() => { if (tar.isMouseOver) { tar.type = 'text'; } }, wait); }, false); tar.addEventListener('mouseout', () => { tar.isMouseOver = false; tar.type = 'password'; }, false); tar.addEventListener('blur', () => { tar.type = 'password'; }, false); tar.addEventListener('keydown', e => { if (e.keyCode === KEY_ENTER) { tar.type = 'password'; } }, false); } function mouseDblClick(tar) { tar.addEventListener('dblclick', () => { tar.type = tar.type === 'password' ? 'text' : 'password'; }, false); tar.addEventListener('blur', () => { tar.type = 'password'; }, false); tar.addEventListener('keydown', e => { if (e.keyCode === KEY_ENTER) { tar.type = 'password'; } }, false); } function mouseFocus(tar) { tar.addEventListener('focus', () => { tar.type = 'text'; }, false); tar.addEventListener('blur', () => { tar.type = 'password'; }, false); tar.addEventListener('keydown', e => { if (e.keyCode === KEY_ENTER) { tar.type = 'password'; } }, false); } function ctrlKeyShift(tar) { let isHide = true; let notPressCtrl = true; let onlyCtrl = true; tar.addEventListener('blur', () => { tar.type = 'password'; isHide = true; notPressCtrl = true; onlyCtrl = true; }, false); tar.addEventListener('keyup', e => { if (e.keyCode === KEY_CTRL) { if (onlyCtrl) { isHide = !isHide; } else { isHide = false; } if (isHide) { tar.type = 'password'; } else { tar.type = 'text'; } notPressCtrl = true; onlyCtrl = true; } }, false); tar.addEventListener('keydown', e => { if (e.keyCode === KEY_ENTER) { tar.type = 'password'; isHide = true; notPressCtrl = true; onlyCtrl = true; } else if (e.keyCode === KEY_CTRL) { if (notPressCtrl) { tar.type = 'text'; notPressCtrl = false; onlyCtrl = true; } } else { onlyCtrl = notPressCtrl; } }, false); } const actionsArr = [mouseOver, mouseDblClick, mouseFocus, ctrlKeyShift]; const doc = window.document; const modified = new WeakSet(); function modifyAllInputs() { const passwordInputs = doc.querySelectorAll('input[type=password]'); passwordInputs.forEach(input => { if (!modified.has(input)) { actionsArr[behave](input); modified.add(input); } }); } function modifyWeb() { modifyAllInputs(); } modifyWeb(); const docObserver = new MutationObserverNew(() => { // NOTE: Despite we can recursively check element from addNodes. // Benchmark shows that it is much fast to just use `querySelectorAll` to find password inputs modifyWeb(); }); docObserver.observe(doc.documentElement, { childList: true, subtree: true, // Some website add input with text type at first, then change its type to password. attributes: true, attributeFilter: ['type'] }); }, init() { this.observer(); this.initValue(); this.showPassword(); this.addPluginStyle(); this.isTopWindow() && this.registerMenuCommand(); } }; main.init(); })();