Password Generator

Generate random passwords with Context Menu

// ==UserScript==
// @name            Password Generator
// @name:tr         Şifre Oluşturucu
// @namespace       https://aktolu.com/
// @version         0.6
// @description     Generate random passwords with Context Menu
// @description:tr  Sağ tık menüsünü kullanarak rastgele şifreler oluşturun
// @author          Muhammed Aktolu
// @match           *://*/*
// @icon            https://static-00.iconduck.com/assets.00/security-password-icon-1959x2048-sq0rdvok.png
// @grant           GM_addElement
// @grant           GM_addStyle
// @grant           GM_registerMenuCommand
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_setClipboard
// ==/UserScript==

/* jshint esversion: 11 */

(function() {
	'use strict';
	
	// Start Global
	const toast = function(text, type = 'success') {
		document.getElementById('myToast')?.remove();
		document.getElementById('myToastStyle')?.remove();
		
		// language=css
		let styleString = `
		#myToast {
			display: inline-block !important;
			padding: 20px !important;
			opacity: .6 !important;
			color: white !important;
			background-color: black !important;
			position: fixed !important;
			top: 20px !important;
			right: 20px !important;
			z-index: 9999999999999999999999 !important;
		}
		
		#myToast.success {
			background-color: green !important;
		}
		
		#myToast.warning {
			background-color: orange !important;
		}
		
		#myToast.danger {
			background-color: darkred !important;
		}
		`;
		let style = multilineCss(styleString, 'myToastStyle');
		
		let block = GM_addElement(document.body, 'div', {
			id: 'myToast',
			class: type,
		});
		
		block.innerText = text;
		
		setTimeout(() => {
			block.remove();
			style.remove();
		}, 5000);
	}
	
	const multilineCss = function(style, id = '') {
		let el = GM_addStyle(style);
		if (id !== '') {
			el.id = id;
		}
		
		return el;
	}
	// End Global
	
	// Start Password Generator
	const generatePassword = function(length = 16, minPerType = 2, uppercase = true, lowercase = true, number = true, symbol = false) {
		let string = '';
		let pass = {
			uppercase: true,
			lowercase: true,
			number: true,
			symbol: true,
		}
		let match;
		if (uppercase) {
			string += 'ABCDEFGHJKMNPQRSTUVWXYZ';
		}
		
		if (lowercase) {
			string += 'abcdefghjkmnpqrstuvwxyz';
		}
		
		if (number) {
			string += '23456789';
		}
		
		if (symbol) {
			string += '!";#$%&\'()*+,-./:;<=>?@[]^_`{|}~';
		}
		
		let password = Array.from(crypto.getRandomValues(new Uint32Array(length)))
			.map((x) => string[x % string.length])
			.join('');
		
		if (uppercase) {
			match = password.match(/[A-Z]/g);
			if (!match || match.length < minPerType) {
				pass.uppercase = false;
			}
		}
		
		if (lowercase) {
			match = password.match(/[a-z]/g);
			if (!match || match.length < minPerType) {
				pass.lowercase = false;
			}
		}
		
		if (number) {
			match = password.match(/\d/g);
			if (!match || match.length < minPerType) {
				pass.number = false;
			}
		}
		
		if (symbol) {
			match = password.match(/[^A-Za-z0-9]/g);
			if (!match || match.length < minPerType) {
				pass.symbol = false;
			}
		}
		
		if (password.match(/(.)\1/) || !pass.uppercase || !pass.lowercase || !pass.number || !pass.symbol) {
			return generatePassword(length, minPerType, uppercase, lowercase, number, symbol);
		} else {
			return password;
		}
	}
	// End Password Generator
	
	GM_registerMenuCommand('Generate Password', function() {
		if (document.getElementById('pgBlock')) {
			return false;
		}
		
		//let length = GM_getValue('passwordGeneratorLength', '16');
		let password = generatePassword(GM_getValue('pgLength', 16), GM_getValue('pgMinPerType', 2), GM_getValue('pgUppercase', true), GM_getValue('pgLowercase', true), GM_getValue('pgNumber', true), GM_getValue('pgSymbol', false));
		
		// language=css
		let styleString = `
		#pgBlock {
			position: fixed !important;
			width: 100vw !important;
			height: 100vh !important;
			z-index: 999999999999999999999 !important;
			display: flex !important;
			align-items: center !important;
			justify-content: center !important;
			background-color: rgba(0, 0, 0, .9) !important;
			left: 0 !important;
			top: 0 !important;
		}
		
		#pgBlock * {
			font-family: Helvetica, Arial, sans-serif !important;
			font-size: 15px !important;
			font-weight: 300 !important;
			letter-spacing: 0 !important;
			line-height: 100% !important;
			color: whitesmoke !important;
			float: none !important;
			border-radius: 0 !important;
		}
		
		#pgBlock div {
			display: block !important;
		}
		
		#pgBlock button {
			background-color: red !important;
			padding: 10px 30px !important;
			border: none !important;
			cursor: pointer !important;
		}
		
		#pgBlock input {
			margin-inline: 0 !important;
			font-size: 13px !important;
			display: inline !important;
		}
		
		#pgBlock input[type="number"] {
			background-color: black !important;
			border: 1px solid whitesmoke !important;
			width: 60px !important;
			padding: 1px 0 1px 2px !important;
			appearance: auto !important;
		}
		
		#pgBlock label {
			margin-right: 15px !important;
			cursor: pointer !important;
			-webkit-user-select: none !important;
			user-select: none !important;
		}

		.pgBox {
			text-align: center !important;
		}

		#pgPassword {
			font-size: 3rem !important;
			margin-bottom: 15px !important;
		}
		`;
		
		let style = multilineCss(styleString);
		let block = GM_addElement(document.body, 'div', {
			id: 'pgBlock',
		});
		
		// language=html
		block.innerHTML = `<div class="pgBox">
			<div id="pgPassword"></div>
			<div>
				<button id="pgCopyPassword">Copy</button>
				<button id="pgCancelPassword">Cancel</button>
			</div>
			<div style="margin-top: 100px">
				<div>
					Length: <input id="pgPasswordLength" type="number" value="`+GM_getValue('pgLength', 16)+`" style="margin-right: 60px" min="8" max="64">
					Min Chars Per Type: <input id="pgPasswordMinPerType" type="number" value="`+GM_getValue('pgMinPerType', 2)+`" min="0">
				</div>
				<div style="margin-top: 10px">
					<label>Uppercase: <input id="pgUppercase" type="checkbox"`+(GM_getValue('pgUppercase', true) ? ' checked' : '')+`></label>
					<label>Lowercase: <input id="pgLowercase" type="checkbox"`+(GM_getValue('pgLowercase', true) ? ' checked' : '')+`></label>
					<label>Number: <input id="pgNumber" type="checkbox"`+(GM_getValue('pgNumber', true) ? ' checked' : '')+`></label>
					<label>Symbol: <input id="pgSymbol" type="checkbox"`+(GM_getValue('pgSymbol', false) ? ' checked' : '')+`></label>
				</div>
				<button id="pgReGeneratePassword" style="margin-top: 25px">Regenerate Password</button>
			</div>
		</div>`;
		
		let passwordDiv = document.getElementById('pgPassword');
		let copyButton = document.getElementById('pgCopyPassword');
		let cancelButton = document.getElementById('pgCancelPassword');
		let passwordLengthInput = document.getElementById('pgPasswordLength');
		let minPerTypeInput = document.getElementById('pgPasswordMinPerType');
		let uppercaseCheckBox = document.getElementById('pgUppercase');
		let lowercaseCheckBox = document.getElementById('pgLowercase');
		let numberCheckBox = document.getElementById('pgNumber');
		let symbolCheckBox = document.getElementById('pgSymbol');
		let reGenerateButton = document.getElementById('pgReGeneratePassword');
		
		passwordDiv.innerText = password;
		
		copyButton.addEventListener('click', () => {
			navigator.clipboard.writeText(password)
				.then(r => toast('Password has been copied successfully.'));
			block.remove();
			style.remove();
		});
		
		cancelButton.addEventListener('click', () => {
			block.remove();
			style.remove();
		});
		
		[passwordLengthInput, minPerTypeInput].forEach(item => {
			item.addEventListener('click', () => {
				item.select();
			});
		});
		
		reGenerateButton.addEventListener('click', function() {
			GM_setValue('pgLength', parseInt(passwordLengthInput.value));
			GM_setValue('pgMinPerType', parseInt(minPerTypeInput.value));
			GM_setValue('pgUppercase', uppercaseCheckBox.checked);
			GM_setValue('pgLowercase', lowercaseCheckBox.checked);
			GM_setValue('pgNumber', numberCheckBox.checked);
			GM_setValue('pgSymbol', symbolCheckBox.checked);
			password = generatePassword(parseInt(passwordLengthInput.value), parseInt(minPerTypeInput.value), uppercaseCheckBox.checked, lowercaseCheckBox.checked, numberCheckBox.checked, symbolCheckBox.checked);
			passwordDiv.innerText = password;
		});
	});
})();