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