main
Raw Download raw file
 1/*
 2 * noVNC: HTML5 VNC client
 3 * Copyright (c) 2025 The noVNC authors
 4 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
 5 */
 6
 7import * as Log from './util/logging.js';
 8import { browserAsyncClipboardSupport } from './util/browser.js';
 9
10export default class AsyncClipboard {
11    constructor(target) {
12        this._target = target || null;
13
14        this._isAvailable = null;
15
16        this._eventHandlers = {
17            'focus': this._handleFocus.bind(this),
18        };
19
20        // ===== EVENT HANDLERS =====
21
22        this.onpaste = () => {};
23    }
24
25    // ===== PRIVATE METHODS =====
26
27    async _ensureAvailable() {
28        if (this._isAvailable !== null) return this._isAvailable;
29        try {
30            const status = await browserAsyncClipboardSupport();
31            this._isAvailable = (status === 'available');
32        } catch {
33            this._isAvailable = false;
34        }
35        return this._isAvailable;
36    }
37
38    async _handleFocus(event) {
39        if (!(await this._ensureAvailable())) return;
40        try {
41            const text = await navigator.clipboard.readText();
42            this.onpaste(text);
43        } catch (error) {
44            Log.Error("Clipboard read failed: ", error);
45        }
46    }
47
48    // ===== PUBLIC METHODS =====
49
50    writeClipboard(text) {
51        // Can lazily check cached availability
52        if (!this._isAvailable) return false;
53        navigator.clipboard.writeText(text)
54            .catch(error => Log.Error("Clipboard write failed: ", error));
55        return true;
56    }
57
58    grab() {
59        if (!this._target) return;
60        this._ensureAvailable()
61            .then((isAvailable) => {
62                if (isAvailable) {
63                    this._target.addEventListener('focus', this._eventHandlers.focus);
64                }
65            });
66    }
67
68    ungrab() {
69        if (!this._target) return;
70        this._target.removeEventListener('focus', this._eventHandlers.focus);
71    }
72}