1/** 2 * Handling of passwords in the displayed page 3 */ 4class PageHandling { 5 6 timer = null; 7 8 /** 9 * Register handlers 10 * 11 * @param {SubtleAES} aes 12 */ 13 constructor(aes) { 14 this.aes = aes; 15 16 jQuery('.encryptedpasswords svg:first-of-type') 17 .on('click', this.showAll.bind(this)) 18 .attr('title', LANG.plugins.encryptedpasswords.decryptAll) 19 ; 20 jQuery('.encryptedpasswords svg:last-of-type') 21 .on('click', this.hideAll.bind(this)) 22 .attr('title', LANG.plugins.encryptedpasswords.hideAll) 23 ; 24 jQuery('.encryptedpasswords span') 25 .on('click', this.copyHandler.bind(this)) 26 .attr('title', LANG.plugins.encryptedpasswords.copy) 27 ; 28 } 29 30 /** 31 * Decrypt and display a single password element in the page 32 * 33 * @param {jQuery} $element 34 * @param {string} passphrase 35 */ 36 async showClear($element, passphrase) { 37 const cipher = $element.data('crypted'); 38 $element.removeClass('error'); 39 $element.attr('title', ''); 40 41 try { 42 const clear = await this.aes.autodecrypt(cipher, passphrase); 43 $element.find('span').text(clear); 44 $element.removeClass('crypted'); 45 $element.addClass('clear'); 46 } catch (e) { 47 $element.addClass('error'); 48 $element.attr('title', LANG.plugins.encryptedpasswords.invalidKey); 49 } 50 } 51 52 /** 53 * Copy a clicked password to clipboard 54 * 55 * @param {Event} e 56 */ 57 async copyHandler(e) { 58 const $element = jQuery(e.target).parent(); 59 let clear = $element.find('span').text(); // get early, timer may interfere 60 const cipher = $element.data('crypted'); 61 62 if ($element.hasClass('crypted')) { 63 const passphrase = await GUI.prompt( 64 LANG.plugins.encryptedpasswords.enterKey, 65 LANG.plugins.encryptedpasswords.passphrase 66 ); 67 if (passphrase === null || passphrase === '') return; 68 try { 69 clear = await this.aes.autodecrypt(cipher, passphrase); 70 } catch (e) { 71 GUI.toast(LANG.plugins.encryptedpasswords.invalidKey, 'error'); 72 return; 73 } 74 } 75 76 try { 77 await navigator.clipboard.writeText(clear); 78 GUI.toast(LANG.plugins.encryptedpasswords.copyOk, 'success'); 79 } catch (e) { 80 console.error(e); 81 GUI.toast(LANG.plugins.encryptedpasswords.copyFail, 'error'); 82 } 83 84 } 85 86 /** 87 * Decrypt and show all passwords in the page 88 */ 89 async showAll() { 90 const self = this; 91 const passphrase = await GUI.prompt( 92 LANG.plugins.encryptedpasswords.enterKey, 93 LANG.plugins.encryptedpasswords.passphrase 94 ); 95 if (passphrase === null || passphrase === '') return; 96 97 jQuery('.encryptedpasswords.crypted').each(function (i, e) { 98 self.showClear(jQuery(e), passphrase); 99 }); 100 101 this.setTimer(); 102 } 103 104 /** 105 * Hide all passwords in the page 106 */ 107 hideAll() { 108 jQuery('.encryptedpasswords.clear') 109 .removeClass('clear') 110 .addClass('crypted') 111 .find('span').text('••••••••••'); 112 this.clearTimer(); 113 } 114 115 /** 116 * Set the timer to hide all passwords again 117 */ 118 setTimer() { 119 const timeout = JSINFO.plugins.encryptedpasswords.timeout; 120 if (!timeout) return; 121 this.clearTimer(); 122 this.timer = window.setTimeout(this.hideAll.bind(this), timeout * 1000); 123 } 124 125 /** 126 * Clear any timer that might be set 127 */ 128 clearTimer() { 129 if (this.timer !== null) { 130 window.clearTimeout(this.timer); 131 this.timer = null; 132 } 133 } 134} 135