// ==UserScript==
// @name ShowPass
// @namespace bitst0rm
// @version 0.0.10
// @description Show password as plain text in password fields
// @author bitst0rm
// @license GPLv3
// @icon https://github.com/bitst0rm-pub/ShowPass/raw/master/logo.png
// @include *
// @run-at document-idle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// ==/UserScript==
(function(win) {
'use strict';
if (typeof GM_getValue === 'undefined' ||
typeof GM_setValue === 'undefined' ||
typeof GM_registerMenuCommand === 'undefined') {
alert('You\'ve been warned 3 times that this script\n' +
'does not support webshit aka Greasemonkey!\n' +
'Get Tampermonkey and you will be happy.\n' +
'Do not do this again.');
return;
}
var MOUSEOVER = 'idMouseOver';
var DBLCLICK = 'idDblClick';
var FOCUS = 'idFocus';
var CTRL = 'idCtrl';
var BOLD = 'idBold';
var PREVIEW = 'idPreview';
var PROGRESS = 'idProgress';
var HEIGHT = 'idHeight';
var COLOR = 'idColor';
var EXCLSHOW = 'idExclShow';
var EXCLSTRENGTH = 'idExclStrength';
var DISTANCE = 'idDistance';
var UID = 'showpass-' + Math.random().toString(36).slice(8);
var RANDOM = null;
var config = {};
config[MOUSEOVER] = true;
config[DBLCLICK] = false;
config[FOCUS] = false;
config[CTRL] = false;
config[BOLD] = true;
config[PROGRESS] = true;
config[HEIGHT] = '6';
config[COLOR] = '#ff0000';
config[EXCLSHOW] = '^login\\.example\\.com\n^(.+?\\.)?example\\.net';
config[EXCLSTRENGTH] = '^(.+?\\.)?dropbox\\.com';
config[DISTANCE] = '^accounts\\.google\\.com#2\n^(.+?\\.)?(gmx\\.(net|at)|web\\.de)#1';
for (var k in config) {
if (GM_getValue(k) === undefined) {
GM_setValue(k, config[k]);
}
}
var html = '<!DOCTYPE html>' +
'<html lang="en">' +
'<head>' +
'<title>ShowPass Option</title>' +
'<meta charset="utf-8">' +
'<meta name="viewport" content="width=device-width">' +
'<style>' +
'body{font-family:Arial,Helvetica,sans-serif;text-align:center;}' +
'table{margin-right:auto;margin-left:auto;padding:1em;text-align:left;background-color:#f8f8f8;border:solid 1px #bebebe;border-radius:1em;}' +
'td{padding:.5em;}' +
'input,textarea,select{margin-left:0px;padding:4px;border-radius:3px;border:1px solid #9aa4b1;}' +
'input[type="color"]{padding:1px 2px;}' +
'textarea{min-width:20em;height:6em;}' +
'label{margin:0 .4em;}' +
'#' + HEIGHT + '{margin-left: .4em;}' +
'.badge{background-color:#adebeb;border:1px solid #009999;border-radius:5px;padding:1px 6px;box-shadow:1px 1px 2px rgba(0,0,0,0.3);}' +
'#msg{font-size:1.2em;font-weight:bold;margin:1em;transition:opacity .5s ease 1s;opacity:0;color:#669900;}' +
'#msg.show{transition:none;opacity:1;}' +
'</style>' +
'</head>' +
'<body>' +
'<h1>ShowPass Option</h1>' +
'<table><tr><td>Show password mode:</td>' +
'<td><select id="menu">' +
'<option id="' + MOUSEOVER + '"' + (GM_getValue(MOUSEOVER) ? ' selected' : '') + '>Mouse Over</option>' +
'<option id="' + DBLCLICK + '"' + (GM_getValue(DBLCLICK) ? ' selected' : '') + '>Double Click</option>' +
'<option id="' + FOCUS + '"' + (GM_getValue(FOCUS) ? ' selected' : '') + '>On Focus</option>' +
'<option id="' + CTRL + '"' + (GM_getValue(CTRL) ? ' selected' : '') + '>Press CTRL Key</option>' +
'</select></td></tr>' +
'<tr><td>Password style:</td>' +
'<td><input type="checkbox" id="' + BOLD + '"' + (GM_getValue(BOLD) ? ' checked' : '') + '>' +
'<label for="' + BOLD + '">Bold</label>' +
'<input type="color" value="' + GM_getValue(COLOR) + '" id="' + COLOR + '">' +
'<label for="' + COLOR + '">Color</label></td></tr>' +
'<tr><td>Show password strength:</td>' +
'<td><input type="checkbox" id="' + PROGRESS + '"' + (GM_getValue(PROGRESS) ? ' checked' : '') + '>' +
'<input type="number" id="' + HEIGHT + '" min="4" max="30" value="' + GM_getValue(HEIGHT) + '">' +
'<label for="' + HEIGHT + '">px Height</label></td></tr>' +
'<tr><td>Preview password:</td>' +
'<td><input type="password" id="' + PREVIEW + '" placeholder="Enter password"></td></tr>' +
'<tr><td>Exclude showing password:<br><i>(RegExp on hostnames)</i></td>' +
'<td><textarea id="' + EXCLSHOW + '">' + GM_getValue(EXCLSHOW) + '</textarea></td></tr>' +
'<tr><td>Exclude showing strength bar:<br><i>(RegExp on hostnames)</i></td>' +
'<td><textarea id="' + EXCLSTRENGTH + '">' + GM_getValue(EXCLSTRENGTH) + '</textarea></td></tr>' +
'<tr><td><span class="badge">Fine-turning</span> Distance between<br>strength bar and pw input field:<br><i>(RegExp hostname#nDistance)</i></td>' +
'<td><textarea id="' + DISTANCE + '">' + GM_getValue(DISTANCE) + '</textarea></td></tr></table>' +
'<div id="msg">Saved!</div>' +
'</body>' +
'</html>';
var css = '.progress-bar-' + UID + '{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:' + GM_getValue(HEIGHT) + 'px;border:1px solid #b2b2b2;border-radius:1em;background-color:#efefef;}' +
'.progress-bar-' + UID + '::-webkit-progress-bar{border-radius:1em;background-color:#efefef;}' +
'.progress-bar-' + UID + '::-webkit-progress-value{-webkit-transition:width 0.5s ease;transition:width 0.5s ease;border-radius:1em 0 0 1em;}' +
'.progress-bar-' + UID + '[value="1"]::-webkit-progress-value{background-color:#dc3545;}' +
'.progress-bar-' + UID + '[value="2"]::-webkit-progress-value{background-color:#ffc107;}' +
'.progress-bar-' + UID + '[value="3"]::-webkit-progress-value{background-color:#1178f7;}' +
'.progress-bar-' + UID + '[value="4"]::-webkit-progress-value{background-color:#17a2b8;}' +
'.progress-bar-' + UID + '[value="5"]::-webkit-progress-value{border-radius:1em;background-color:#28a745;}' +
'.progress-bar-' + UID + '::-moz-progress-bar{-moz-transition:width 0.5s ease;border-radius:1em 0 0 1em;}' +
'.progress-bar-' + UID + '[value="1"]::-moz-progress-bar{background-color:#dc3545;}' +
'.progress-bar-' + UID + '[value="2"]::-moz-progress-bar{background-color:#ffc107;}' +
'.progress-bar-' + UID + '[value="3"]::-moz-progress-bar{background-color:#1178f7;}' +
'.progress-bar-' + UID + '[value="4"]::-moz-progress-bar{background-color:#17a2b8;}' +
'.progress-bar-' + UID + '[value="5"]::-moz-progress-bar{border-radius:1em;background-color:#28a745;}' +
'#br-' + UID + '{display:block;content:"";margin:0px;line-height:0px;}';
function blacklist(type, host) {
var txt = GM_getValue(type);
var lines = txt.split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var re = new RegExp(line);
if (line && re.test(host)) return true;
}
return false;
}
function strength(pw) {
return (/.{8,}/.test(pw) * ( /* at least 8 characters */
/.{12,}/.test(pw) + /* bonus if longer */
/[a-z]/.test(pw) + /* a lower letter */
/[A-Z]/.test(pw) + /* a upper letter */
/\d/.test(pw) + /* a digit */
/[^A-Za-z0-9]/.test(pw))) - /* a special character */
/(.)\1{2,}/.test(pw); /* a same char 3 times or more */
}
function distance(input, host) {
var txt = GM_getValue(DISTANCE);
var lines = txt.split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var p = line.split('#');
var hostname = p[0];
var level = Number(p[1]);
if (isNaN(level) || level < 1) {
console.warn('[ShowPass] Distance must be of type number and greater than 0: ', line);
continue;
}
var re = new RegExp(hostname);
if (hostname && re.test(host)) {
var a = null;
var b = null;
var c = 0;
var current = input;
while (current.parentNode) {
if (c === level + 1) break;
b = current;
current = current.parentNode;
a = current;
c++;
}
if (a.constructor.name !== 'HTMLDocument') {
return [a, b];
} else {
console.error('[ShowPass] Distance out of the scope: ', line);
return false;
}
}
}
return false;
}
function progress(input, host) {
RANDOM = Math.random().toString(36).slice(2);
input.style.setProperty('margin-bottom', '0px', 'important');
var inputWidth = input.offsetWidth;
var div = document.createElement('div');
div.id = 'progress-wrapper-' + RANDOM;
div.style.margin = '0px';
div.style.padding = '0px';
div.style.boxSizing = 'border-box';
var br = document.createElement('br');
br.id = 'br-' + UID;
var pgr = document.createElement('progress');
pgr.id = 'progress-' + RANDOM;
pgr.className = 'progress-bar-' + UID;
pgr.value = 0;
pgr.max = 5;
pgr.style.width = inputWidth + 'px';
pgr.style.margin = '2px 0px 0px 0px';
pgr.style.padding = '0px';
pgr.style.verticalAlign = 'top';
var arr = distance(input, host);
if (arr) {
arr[0].insertBefore(div, arr[1].nextSibling);
} else {
input.parentNode.insertBefore(div, input.nextSibling);
div.appendChild(input);
div.appendChild(br);
}
div.appendChild(pgr);
input.addEventListener('keyup', function() {
pgr.value = strength(input.value);
});
return pgr;
}
function update(input, host) {
var pgr = progress(input, host);
var c = 0;
var check = setInterval(function() {
if (c === 20) clearInterval(check);
pgr.style.width = input.offsetWidth + 'px';
c++;
}, 100);
}
function cfg() {
var display = true;
document.body.parentElement.innerHTML = html;
GM_addStyle(css);
var pgrbox = document.getElementById(PROGRESS);
var hight = document.getElementById(HEIGHT);
hight.disabled = (pgrbox.checked ? false : true);
document.body.addEventListener('change', function(e) {
var t = e.target;
GM_setValue(t.id, (t.type === 'checkbox' ? t.checked : t.value));
if (t.id === 'menu') {
for (var i = 0; i < t.length; i++) {
var option = t.options[i];
GM_setValue(option.id, option.selected);
}
}
var pgr = document.getElementById('progress-' + RANDOM);
if (t.id === PROGRESS) {
if (!pgr) {
var input = document.getElementById(PREVIEW);
pgr = progress(input);
display = false;
}
if (!display) {
pgr.style.display = 'block';
hight.disabled = false;
} else {
pgr.style.display = 'none';
hight.disabled = true;
}
display = !display;
}
if (t.id === HEIGHT) pgr.style.height = t.value + 'px';
var elem = document.getElementById('msg');
elem.classList.add('show');
setTimeout(function() {
elem.classList.remove('show');
}, 100);
});
}
function setCaretToEnd(e) {
setTimeout((function() {
var t = e.target;
var l = t.value.length;
t.setSelectionRange(l, l);
}), 0);
}
function show(e) {
var t = e.target;
t.type = 'text';
t.style.setProperty('caret-color', 'black', 'important');
t.style.setProperty('color', GM_getValue(COLOR), 'important');
t.style.setProperty('font-weight', GM_getValue(BOLD) ? 'bold' : '', 'important');
setCaretToEnd(e);
}
function hide(e) {
var t = e.target;
t.type = 'password';
t.style.caretColor = '';
t.style.color = '';
t.style.fontWeight = '';
}
function showPass(node, host) {
var isHide = true;
var _toggle = function(e) {
if (e.keyCode === 17) {
if (isHide) {
show(e);
} else {
hide(e);
}
isHide = !isHide;
}
};
var _hide = function(e) {
hide(e);
isHide = true;
};
var inputs = node.querySelectorAll('input[type=password]');
if (!inputs.length) return;
for (var j = 0; j < inputs.length; j++) {
var input = inputs[j];
if (input.ariaHidden && input.ariaHidden === 'true' || input.hidden ||
input.style.visibility && input.style.visibility === 'hidden' ||
input.style.display && input.style.display === 'none') continue;
if (!input.ready) {
input.ready = true;
if (GM_getValue(MOUSEOVER)) {
input.addEventListener('mouseover', show, false);
input.addEventListener('mouseout', hide, false);
}
if (GM_getValue(DBLCLICK)) {
input.addEventListener('dblclick', show, false);
input.addEventListener('blur', hide, false);
}
if (GM_getValue(FOCUS)) {
input.addEventListener('focus', show, false);
input.addEventListener('blur', hide, false);
}
if (GM_getValue(CTRL)) {
input.addEventListener('keyup', _toggle, false);
input.addEventListener('blur', _hide, false);
}
if (GM_getValue(PROGRESS)) {
if (blacklist(EXCLSTRENGTH, host)) continue;
update(input, host);
}
}
}
}
win.addEventListener('resize', function() {
var bars = document.getElementsByClassName('progress-bar-' + UID);
for (var i = 0; i < bars.length; i++) {
var bar = bars[i];
var input = bar.parentNode.querySelector('input[type="password"]');
bar.style.width = input.offsetWidth + 'px';
}
});
function domWatcher() {
var queue = [];
var ignoreTags = ['br', 'head', 'link', 'meta', 'script', 'style'];
var Watch = win.MutationObserver || win.WebKitMutationObserver;
var observer = new Watch(function(mutations) {
if (!queue.length) requestAnimationFrame(process);
queue.push(mutations);
});
var container = document.documentElement || document.body;
observer.observe(container, {
childList: true,
subtree: true
});
function process() {
for (var i = 0; i < queue.length; i++) {
var mutations = queue[i];
for (var j = 0; j < mutations.length; j++) {
var mutation = mutations[j];
if (mutation.type === 'childList') {
var addedNodes = mutation.addedNodes;
for (var n = 0; n < addedNodes.length; n++) {
var node = addedNodes[n];
if (node.nodeType !== 1) continue;
if (ignoreTags.indexOf(node.localName) !== -1) continue;
if (node.parentElement === null) continue;
if (node.nodeName === 'IFRAME') {
goFrame(node);
} else {
goPage(node);
}
}
}
}
}
queue.length = 0;
}
}
function goFrame(node) {
var frame = node.contentDocument || node.contentWindow.document;
if (blacklist(EXCLSHOW, frame.location.hostname)) return;
showPass(frame.body, frame.location.hostname);
}
function goPage(node) {
showPass(node, win.location.hostname);
}
function init() {
GM_registerMenuCommand('Configure', cfg, 'C');
if (blacklist(EXCLSHOW, win.location.hostname)) return;
if (GM_getValue(PROGRESS)) GM_addStyle(css);
goPage(document.body);
var nodes = document.querySelectorAll('iframe');
for (var i = 0; i < nodes.length; i++) {
goFrame(nodes[i]);
}
domWatcher();
}
init();
})(this);