<!DOCTYPE html>
<html lang="en" class="noVNC_loading">
<head>
    <title>Console: {{ hostname }}</title>

    <link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='images/dracs.svg') }}">
    <meta name="theme-color" content="#313131">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

    <link rel="stylesheet" href="{{ url_for('static', filename='novnc/app/styles/constants.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='novnc/app/styles/base.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='novnc/app/styles/input.css') }}">

    <style>
        #dracs-hostname-bar {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            background: #313131;
            color: #ccc;
            font-family: monospace;
            font-size: 12px;
            padding: 0 8px;
            z-index: 9999;
            text-shadow: 0 0 4px rgba(0, 0, 0, 0.9);
            transform: translateY(-100%);
            transition: transform 0.2s ease;
        }
        #dracs-hostname-bar.visible {
            transform: translateY(0);
        }
        #dracs-hostname-line {
            height: 22px;
            line-height: 22px;
            text-align: center;
            cursor: default;
        }
        #dracs-remoteimage-row {
            height: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            font-size: 11px;
        }
        #dracs-remoteimage-row input[type="checkbox"] {
            cursor: pointer;
        }
        #dracs-remoteimage-row select,
        #dracs-remoteimage-row input[type="text"] {
            background: #1e1e1e;
            color: #ddd;
            border: 1px solid #555;
            border-radius: 3px;
            font-family: monospace;
            font-size: 11px;
            padding: 1px 4px;
            cursor: text;
        }
        #dracs-remoteimage-row select {
            cursor: pointer;
        }
        #dracs-remoteimage-row button {
            background: #333;
            color: #bbb;
            border: 1px solid #555;
            border-radius: 3px;
            font-family: monospace;
            font-size: 11px;
            padding: 2px 8px;
            cursor: pointer;
        }
        #dracs-remoteimage-row button:hover {
            background: #444;
            color: #ddd;
        }
        #dracs-remoteimage-row button:disabled {
            opacity: 0.5;
            cursor: default;
        }
        #dracs-hostname-handle {
            position: fixed;
            top: 0;
            left: 50%;
            transform: translateX(-50%);
            width: 32px;
            height: 14px;
            background: rgba(49, 49, 49, 0.75);
            border-radius: 0 0 6px 6px;
            cursor: pointer;
            z-index: 9999;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: top 0.2s ease, background 0.2s ease;
        }
        #dracs-hostname-handle:hover {
            background: rgba(49, 49, 49, 0.95);
        }
        #dracs-hostname-bar.visible ~ #dracs-hostname-handle {
            top: 50px;
        }
        #dracs-hostname-handle::after {
            content: '';
            display: block;
            width: 0;
            height: 0;
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
            border-top: 5px solid #ccc;
        }
        #dracs-hostname-bar.visible ~ #dracs-hostname-handle::after {
            border-top: none;
            border-bottom: 5px solid #ccc;
        }
        #dracs-power-btn {
            position: absolute;
            top: 4px;
            right: 8px;
            height: 16px;
            line-height: 14px;
            padding: 0 7px;
            background: #3a3a3a;
            color: #bbb;
            border: 1px solid #666;
            border-radius: 3px;
            font-family: monospace;
            font-size: 10px;
            cursor: pointer;
            z-index: 10000;
        }
        #dracs-power-btn:hover {
            background: #4a4a4a;
            color: #eee;
        }
        #dracs-pwr-modal {
            display: none;
            position: fixed;
            inset: 0;
            z-index: 10000;
            background: rgba(0, 0, 0, 0.6);
            align-items: center;
            justify-content: center;
        }
        #dracs-pwr-modal.open {
            display: flex;
        }
        #dracs-pwr-modal-box {
            background: #2a2a2a;
            border: 1px solid #555;
            border-radius: 6px;
            padding: 20px 24px;
            min-width: 420px;
            font-family: monospace;
        }
        #dracs-pwr-modal-box .pwr-title {
            color: #ccc;
            font-size: 13px;
            font-weight: bold;
            margin-bottom: 8px;
        }
        #dracs-pwr-status-line {
            color: #888;
            font-size: 11px;
            margin-bottom: 14px;
            min-height: 16px;
        }
        #dracs-pwr-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-bottom: 10px;
        }
        #dracs-pwr-buttons button {
            background: #333;
            color: #bbb;
            border: 1px solid #555;
            border-radius: 3px;
            font-family: monospace;
            font-size: 11px;
            padding: 4px 10px;
            cursor: pointer;
        }
        #dracs-pwr-buttons button:hover {
            background: #444;
            color: #eee;
        }
        #dracs-pwr-buttons .pwr-cancel {
            background: #2e2e2e;
            border-color: #666;
        }
        #dracs-pwr-buttons .pwr-danger {
            border-color: #844;
            color: #e88;
        }
        #dracs-pwr-buttons .pwr-danger:hover {
            background: #4a2a2a;
            color: #faa;
        }
        #dracs-pwr-result {
            font-size: 11px;
            min-height: 16px;
            color: #aaa;
        }
        #dracs-ri-modal {
            display: none;
            position: fixed;
            inset: 0;
            z-index: 10000;
            background: rgba(0, 0, 0, 0.6);
            align-items: center;
            justify-content: center;
        }
        #dracs-ri-modal.open {
            display: flex;
        }
        #dracs-ri-modal-box {
            background: #2a2a2a;
            border: 1px solid #555;
            border-radius: 6px;
            padding: 18px 20px;
            min-width: 380px;
            font-family: monospace;
        }
        #dracs-ri-modal-box .ri-modal-label {
            color: #aaa;
            font-size: 12px;
            margin-bottom: 8px;
        }
        #dracs-ri-modal-input {
            width: 100%;
            box-sizing: border-box;
            background: #1e1e1e;
            color: #eee;
            border: 1px solid #666;
            border-radius: 3px;
            padding: 5px 8px;
            font-family: monospace;
            font-size: 12px;
        }
        #dracs-ri-modal-buttons {
            margin-top: 10px;
            display: flex;
            justify-content: flex-end;
            gap: 6px;
        }
        #dracs-ri-modal-buttons button {
            background: #333;
            color: #bbb;
            border: 1px solid #555;
            border-radius: 3px;
            padding: 3px 12px;
            cursor: pointer;
            font-size: 11px;
            font-family: monospace;
        }
        #dracs-ri-modal-buttons button:hover {
            background: #444;
            color: #ddd;
        }
    </style>

    <link rel="preload" as="image" href="{{ url_for('static', filename='novnc/app/images/info.svg') }}">
    <link rel="preload" as="image" href="{{ url_for('static', filename='novnc/app/images/error.svg') }}">
    <link rel="preload" as="image" href="{{ url_for('static', filename='novnc/app/images/warning.svg') }}">

    <script type="module" crossorigin="anonymous" src="{{ url_for('static', filename='novnc/app/error-handler.js') }}"></script>

    <script type="module">
        // Use absolute import paths for Flask's static serving
        const base = "{{ url_for('static', filename='novnc/') }}";

        // Dynamic import with the correct base URL
        const UI = (await import(base + "app/ui.js")).default;

        const mandatory = {
            host: window.location.hostname,
            port: window.location.port || (window.location.protocol === "https:" ? 443 : 80),
            path: "websockify?token={{ token }}",
            encrypt: window.location.protocol === "https:",
            autoconnect: true,
            resize: "scale",
            password: "{{ vnc_password }}",
        };

        let defaults = {};
        try {
            const resp = await fetch(base + "defaults.json");
            if (resp.ok) defaults = await resp.json();
        } catch (e) { /* use empty defaults */ }

        UI.start({ settings: { defaults: defaults, mandatory: mandatory } });

        const KeyTable = (await import(base + "core/input/keysym.js")).default;

        document.getElementById("noVNC_clipboard_send_button")
            .addEventListener("click", async function() {
                if (!UI.rfb) return;
                const btn = this;
                const text = document.getElementById("noVNC_clipboard_text").value;
                if (!text) return;
                btn.disabled = true;
                const total = text.length;
                btn.value = "Sending 0%";
                for (let i = 0; i < total; i++) {
                    const ch = text.charAt(i);
                    if (ch === "\n" || ch === "\r") {
                        UI.rfb.sendKey(KeyTable.XK_Return, "Enter");
                    } else if (ch === "\t") {
                        UI.rfb.sendKey(KeyTable.XK_Tab, "Tab");
                    } else {
                        UI.rfb.sendKey(ch.charCodeAt(0), "Unidentified");
                    }
                    if (i % 20 === 19) {
                        btn.value = "Sending " + Math.round(((i + 1) / total) * 100) + "%";
                        await new Promise(r => setTimeout(r, 10));
                    }
                }
                document.getElementById("noVNC_clipboard_text").value = "";
                btn.value = "Send";
                btn.disabled = false;
            });
    </script>
</head>

<body>

    <div id="dracs-hostname-bar">
        <button id="dracs-power-btn" onclick="pwrShowModal()" title="Power Action">Power</button>
        <div id="dracs-hostname-line">
            <span>{{ hostname }}</span>
            <span id="dracs-viewers-badge" title="Active viewers"
                  style="margin-left:10px;font-size:11px;color:#aaa;
                         background:#222;border:1px solid #444;
                         border-radius:10px;padding:1px 7px;display:none;"></span>
        </div>
        <div id="dracs-remoteimage-row">
            <span id="dracs-ri-content"></span>
        </div>
    </div>
    <div id="dracs-hostname-handle" onclick="toggleHostnameBar()" title="Show/Hide hostname bar"></div>

    <div id="dracs-pwr-modal">
        <div id="dracs-pwr-modal-box">
            <div class="pwr-title">Power Action: <span id="dracs-pwr-hostname-label"></span></div>
            <div id="dracs-pwr-status-line">Checking power status...</div>
            <div id="dracs-pwr-buttons">
                <button class="pwr-cancel" onclick="pwrCancelModal()">Cancel</button>
                <button onclick="pwrApplyAction('powerup')">Power On</button>
                <button onclick="pwrApplyAction('powercycle')">Graceful Reboot</button>
                <button onclick="pwrApplyAction('hardreset')">Hard Reboot</button>
                <button class="pwr-danger" onclick="pwrApplyAction('graceshutdown')">Graceful Shutdown</button>
                <button class="pwr-danger" onclick="pwrApplyAction('powerdown')">Hard Power Off</button>
            </div>
            <div id="dracs-pwr-result"></div>
        </div>
    </div>

    <div id="dracs-ri-modal">
        <div id="dracs-ri-modal-box">
            <div class="ri-modal-label">Custom ISO URL</div>
            <input type="text" id="dracs-ri-modal-input" placeholder="http://server/path/image.iso">
            <div id="dracs-ri-modal-buttons">
                <button onclick="riCancelCustomUrl()">Cancel</button>
                <button onclick="riConfirmCustomUrl()">OK</button>
            </div>
        </div>
    </div>

    <script>
        (function() {
            var targetTitle = 'Console: {{ hostname }}';
            var titleEl = document.querySelector('title');
            if (titleEl) {
                new MutationObserver(function() {
                    if (document.title !== targetTitle) {
                        document.title = targetTitle;
                    }
                }).observe(titleEl, { childList: true, characterData: true, subtree: true });
            }
        })();

        var riHostname = {{ hostname | tojson }};
        var consoleToken = {{ token | tojson }};

        // When this page is opened as a shared/popout window it carries ?shared=1.
        // Register an additional reference so closing this window only decrements
        // the count rather than force-removing the session that other windows still
        // hold.
        var isSharedSession = new URLSearchParams(window.location.search).get('shared') === '1';
        if (isSharedSession) {
            fetch('/api/vnc-session/' + encodeURIComponent(consoleToken) + '/ref', {
                method: 'POST'
            });
        }

        // Release one reference when this window closes.  With reference counting
        // the session persists as long as at least one other window holds it.
        window.addEventListener('beforeunload', function() {
            fetch('/api/vnc-session/' + encodeURIComponent(consoleToken), {
                method: 'DELETE',
                keepalive: true
            });
        });

        // Heartbeat: reset the server-side expiry timer every 5 minutes so
        // VNC_TIMEOUT is measured from last active use, not from session creation.
        setInterval(function() {
            fetch('/api/vnc-session/' + encodeURIComponent(consoleToken), {
                method: 'PATCH'
            });
        }, 5 * 60 * 1000);

        // Viewer count badge: poll every 5 seconds so all viewers see updates promptly.
        function refreshViewerCount() {
            fetch('/api/vnc-session/' + encodeURIComponent(consoleToken) + '/viewers')
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    var badge = document.getElementById('dracs-viewers-badge');
                    if (badge && data.viewers !== undefined) {
                        var n = data.viewers;
                        badge.textContent = n + (n === 1 ? ' viewer' : ' viewers');
                        badge.style.display = '';
                    }
                })
                .catch(function() {});
        }
        refreshViewerCount();
        setInterval(refreshViewerCount, 5000);
        var riServerEnabled = false;
        var riServerUrl = '';
        var riUIEnabled = false;
        var riUserChangedEnabled = false;
        var riIsoImages = [];
        var riCustomUrl = '';
        var riLoaded = false;

        function riEscape(str) {
            return String(str)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;');
        }

        function toggleHostnameBar() {
            var bar = document.getElementById('dracs-hostname-bar');
            bar.classList.toggle('visible');
            if (bar.classList.contains('visible') && !riLoaded) {
                riLoadStatus();
                riLoadIsoImages();
            }
        }

        function riLoadStatus() {
            var content = document.getElementById('dracs-ri-content');
            content.innerHTML = 'Loading...';
            fetch('/api/remoteimage/' + encodeURIComponent(riHostname))
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data.success) {
                        riServerEnabled = data.enabled;
                        riServerUrl = data.url || '';
                        riUIEnabled = data.enabled;
                        riUserChangedEnabled = false;
                        riLoaded = true;
                        riRender();
                    } else {
                        content.innerHTML = '<span style="color:#f88">Error: ' + riEscape(data.message) + '</span>';
                    }
                })
                .catch(function(e) {
                    content.innerHTML = '<span style="color:#f88">Error: ' + riEscape(e.message) + '</span>';
                });
        }

        function riLoadIsoImages() {
            fetch('/api/iso-images')
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data.success) {
                        riIsoImages = data.images;
                    }
                })
                .catch(function() { riIsoImages = []; });
        }

        function riRender() {
            var content = document.getElementById('dracs-ri-content');
            var checkedAttr = riUIEnabled ? ' checked' : '';
            var html = '<label style="cursor:pointer;">'
                + '<input type="checkbox" id="dracs-ri-checkbox"' + checkedAttr
                + ' onchange="riCheckboxChanged()"> Remote Image Enabled</label>';

            if (riUIEnabled) {
                if (!riUserChangedEnabled) {
                    html += ' <span style="color:#aaa;">URL: '
                        + riEscape(riServerUrl || '(none)') + '</span>';
                } else if (riCustomUrl) {
                    html += ' <input type="text" id="dracs-ri-url-text" value="'
                        + riEscape(riCustomUrl)
                        + '" style="width:260px;" readonly>'
                        + ' <button onclick="riShowCustomUrlModal()">Change...</button>';
                } else {
                    html += ' ' + riRenderDropdown();
                }
            }

            html += ' <button id="dracs-ri-apply-btn" onclick="riApplyChanges()">Apply Changes</button>';
            content.innerHTML = html;
        }

        function riRenderDropdown() {
            var html = '<select id="dracs-ri-iso-select" onchange="riDropdownChanged()">';
            html += '<option value="">-- Select an ISO --</option>';
            html += '<option value="__custom__">Custom ISO URL...</option>';
            for (var i = 0; i < riIsoImages.length; i++) {
                html += '<option value="' + riEscape(riIsoImages[i].url) + '">'
                    + riEscape(riIsoImages[i].name) + '</option>';
            }
            html += '</select>';
            return html;
        }

        function riCheckboxChanged() {
            var cb = document.getElementById('dracs-ri-checkbox');
            riUIEnabled = cb.checked;
            riUserChangedEnabled = true;
            if (!riUIEnabled) {
                riCustomUrl = '';
            }
            riRender();
        }

        function riDropdownChanged() {
            var sel = document.getElementById('dracs-ri-iso-select');
            if (sel && sel.value === '__custom__') {
                riShowCustomUrlModal();
            }
        }

        function riShowCustomUrlModal() {
            document.getElementById('dracs-ri-modal-input').value = riCustomUrl;
            document.getElementById('dracs-ri-modal').classList.add('open');
            document.getElementById('dracs-ri-modal-input').focus();
        }

        function riCancelCustomUrl() {
            document.getElementById('dracs-ri-modal').classList.remove('open');
            riRender();
        }

        function riConfirmCustomUrl() {
            var val = document.getElementById('dracs-ri-modal-input').value.trim();
            document.getElementById('dracs-ri-modal').classList.remove('open');
            riCustomUrl = val;
            riRender();
        }

        function riApplyChanges() {
            var btn = document.getElementById('dracs-ri-apply-btn');
            var data;

            if (!riUIEnabled) {
                data = {action: 'disable'};
            } else {
                var url = '';
                if (!riUserChangedEnabled) {
                    url = riServerUrl;
                } else if (riCustomUrl) {
                    url = riCustomUrl;
                } else {
                    var sel = document.getElementById('dracs-ri-iso-select');
                    if (sel && sel.value !== '__custom__') {
                        url = sel.value;
                    }
                }
                if (!url) {
                    return;
                }
                data = {action: 'enable', url: url};
            }

            if (btn) {
                btn.disabled = true;
                btn.textContent = 'Applying...';
            }

            fetch('/api/remoteimage/' + encodeURIComponent(riHostname), {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(data)
            })
            .then(function(r) { return r.json(); })
            .then(function(resp) {
                if (resp.success) {
                    riLoaded = false;
                    riLoadStatus();
                } else {
                    var content = document.getElementById('dracs-ri-content');
                    content.innerHTML += ' <span style="color:#f88;">'
                        + riEscape(resp.message) + '</span>';
                    if (btn) {
                        btn.disabled = false;
                        btn.textContent = 'Apply Changes';
                    }
                }
            })
            .catch(function(e) {
                var content = document.getElementById('dracs-ri-content');
                content.innerHTML += ' <span style="color:#f88;">Error: '
                    + riEscape(e.message) + '</span>';
                if (btn) {
                    btn.disabled = false;
                    btn.textContent = 'Apply Changes';
                }
            });
        }

        function pwrShowModal() {
            document.getElementById('dracs-pwr-hostname-label').textContent = riHostname;
            document.getElementById('dracs-pwr-status-line').textContent = 'Checking power status...';
            document.getElementById('dracs-pwr-result').textContent = '';
            document.getElementById('dracs-pwr-modal').classList.add('open');
            fetch('/api/power-status', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({hostname: riHostname})
            })
            .then(function(r) { return r.json(); })
            .then(function(data) {
                var line = document.getElementById('dracs-pwr-status-line');
                if (data.success) {
                    line.textContent = 'Current status: ' + (data.status === 'on' ? 'Powered On' : 'Powered Off');
                } else {
                    line.textContent = 'Status: ' + riEscape(data.message);
                }
            })
            .catch(function() {
                document.getElementById('dracs-pwr-status-line').textContent = 'Unable to retrieve power status.';
            });
        }

        function pwrCancelModal() {
            document.getElementById('dracs-pwr-modal').classList.remove('open');
        }

        function pwrApplyAction(action) {
            var result = document.getElementById('dracs-pwr-result');
            result.textContent = 'Sending command...';
            result.style.color = '#aaa';
            fetch('/api/power-action', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({hostname: riHostname, action: action})
            })
            .then(function(r) { return r.json(); })
            .then(function(data) {
                result.textContent = data.success ? data.message : ('Error: ' + data.message);
                result.style.color = data.success ? '#8d8' : '#f88';
                if (data.success) {
                    setTimeout(pwrCancelModal, 2500);
                }
            })
            .catch(function(e) {
                result.textContent = 'Error: ' + e.message;
                result.style.color = '#f88';
            });
        }
    </script>

    <div id="noVNC_fallback_error" class="noVNC_center">
        <div>
            <div>noVNC encountered an error:</div>
            <br>
            <div id="noVNC_fallback_errormsg"></div>
        </div>
    </div>

    <div id="noVNC_control_bar_anchor" class="noVNC_vcenter">

        <div id="noVNC_control_bar">
            <div id="noVNC_control_bar_handle" title="Hide/Show the control bar"><div></div></div>

            <div class="noVNC_scroll">

            <h1 class="noVNC_logo" translate="no"><span>no</span><br>VNC</h1>

            <hr>

            <input type="image" alt="Drag" src="{{ url_for('static', filename='novnc/app/images/drag.svg') }}"
                id="noVNC_view_drag_button" class="noVNC_button noVNC_hidden"
                title="Move/Drag viewport">

            <div id="noVNC_mobile_buttons">
                <input type="image" alt="Keyboard" src="{{ url_for('static', filename='novnc/app/images/keyboard.svg') }}"
                    id="noVNC_keyboard_button" class="noVNC_button" title="Show keyboard">
            </div>

            <input type="image" alt="Extra keys" src="{{ url_for('static', filename='novnc/app/images/toggleextrakeys.svg') }}"
                id="noVNC_toggle_extra_keys_button" class="noVNC_button"
                title="Show extra keys">
            <div class="noVNC_vcenter">
            <div id="noVNC_modifiers" class="noVNC_panel">
                <input type="image" alt="Ctrl" src="{{ url_for('static', filename='novnc/app/images/ctrl.svg') }}"
                    id="noVNC_toggle_ctrl_button" class="noVNC_button"
                    title="Toggle Ctrl">
                <input type="image" alt="Alt" src="{{ url_for('static', filename='novnc/app/images/alt.svg') }}"
                    id="noVNC_toggle_alt_button" class="noVNC_button"
                    title="Toggle Alt">
                <input type="image" alt="Windows" src="{{ url_for('static', filename='novnc/app/images/windows.svg') }}"
                    id="noVNC_toggle_windows_button" class="noVNC_button"
                    title="Toggle Windows">
                <input type="image" alt="Tab" src="{{ url_for('static', filename='novnc/app/images/tab.svg') }}"
                    id="noVNC_send_tab_button" class="noVNC_button"
                    title="Send Tab">
                <input type="image" alt="Esc" src="{{ url_for('static', filename='novnc/app/images/esc.svg') }}"
                    id="noVNC_send_esc_button" class="noVNC_button"
                    title="Send Escape">
                <input type="image" alt="Ctrl+Alt+Del" src="{{ url_for('static', filename='novnc/app/images/ctrlaltdel.svg') }}"
                    id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
                    title="Send Ctrl-Alt-Del">
            </div>
            </div>

            <input type="image" alt="Shutdown/Reboot" src="{{ url_for('static', filename='novnc/app/images/power.svg') }}"
                id="noVNC_power_button" class="noVNC_button"
                title="Shutdown/Reboot...">
            <div class="noVNC_vcenter">
            <div id="noVNC_power" class="noVNC_panel">
                <div class="noVNC_heading">
                    <img alt="" src="{{ url_for('static', filename='novnc/app/images/power.svg') }}"> Power
                </div>
                <input type="button" id="noVNC_shutdown_button" value="Shutdown">
                <input type="button" id="noVNC_reboot_button" value="Reboot">
                <input type="button" id="noVNC_reset_button" value="Reset">
            </div>
            </div>

            <input type="image" alt="Clipboard" src="{{ url_for('static', filename='novnc/app/images/clipboard.svg') }}"
                id="noVNC_clipboard_button" class="noVNC_button"
                title="Clipboard">
            <div class="noVNC_vcenter">
            <div id="noVNC_clipboard" class="noVNC_panel">
                <div class="noVNC_heading">
                    <img alt="" src="{{ url_for('static', filename='novnc/app/images/clipboard.svg') }}"> Clipboard
                </div>
                <p class="noVNC_subheading">
                    Paste text below and click Send to type it into the console.
                </p>
                <textarea id="noVNC_clipboard_text" rows=5></textarea>
                <input type="button" id="noVNC_clipboard_send_button" value="Send"
                    style="margin-top: 4px; width: 100%;">
            </div>
            </div>

            <input type="image" alt="Full screen" src="{{ url_for('static', filename='novnc/app/images/fullscreen.svg') }}"
                id="noVNC_fullscreen_button" class="noVNC_button noVNC_hidden"
                title="Full screen">

            <input type="image" alt="Settings" src="{{ url_for('static', filename='novnc/app/images/settings.svg') }}"
                id="noVNC_settings_button" class="noVNC_button"
                title="Settings">
            <div class="noVNC_vcenter">
            <div id="noVNC_settings" class="noVNC_panel">
                <div class="noVNC_heading">
                    <img alt="" src="{{ url_for('static', filename='novnc/app/images/settings.svg') }}"> Settings
                </div>
                <ul>
                    <li>
                        <label>
                            <input id="noVNC_setting_shared" type="checkbox"
                                   class="toggle">
                            Shared mode
                        </label>
                    </li>
                    <li>
                        <label>
                            <input id="noVNC_setting_view_only" type="checkbox"
                                   class="toggle">
                            View only
                        </label>
                    </li>
                    <li><hr></li>
                    <li>
                        <label>
                            <input id="noVNC_setting_view_clip" type="checkbox"
                                   class="toggle">
                            Clip to window
                        </label>
                    </li>
                    <li>
                        <label for="noVNC_setting_resize">Scaling mode:</label>
                        <select id="noVNC_setting_resize" name="vncResize">
                            <option value="off">None</option>
                            <option value="scale">Local scaling</option>
                            <option value="remote">Remote resizing</option>
                        </select>
                    </li>
                    <li><hr></li>
                    <li>
                        <div class="noVNC_expander">Advanced</div>
                        <div><ul>
                            <li>
                                <label for="noVNC_setting_quality">Quality:</label>
                                <input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
                            </li>
                            <li>
                                <label for="noVNC_setting_compression">Compression level:</label>
                                <input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">
                            </li>
                            <li><hr></li>
                            <li>
                                <label for="noVNC_setting_repeaterID">Repeater ID:</label>
                                <input id="noVNC_setting_repeaterID" type="text" value="">
                            </li>
                            <li>
                                <div class="noVNC_expander">WebSocket</div>
                                <div><ul>
                                    <li>
                                        <label>
                                            <input id="noVNC_setting_encrypt" type="checkbox"
                                                   class="toggle">
                                            Encrypt
                                        </label>
                                    </li>
                                    <li>
                                        <label for="noVNC_setting_host">Host:</label>
                                        <input id="noVNC_setting_host">
                                    </li>
                                    <li>
                                        <label for="noVNC_setting_port">Port:</label>
                                        <input id="noVNC_setting_port" type="number">
                                    </li>
                                    <li>
                                        <label for="noVNC_setting_path">Path:</label>
                                        <input id="noVNC_setting_path" type="text" value="websockify">
                                    </li>
                                </ul></div>
                            </li>
                            <li><hr></li>
                            <li>
                                <label>
                                    <input id="noVNC_setting_reconnect" type="checkbox"
                                           class="toggle">
                                    Automatic reconnect
                                </label>
                            </li>
                            <li>
                                <label for="noVNC_setting_reconnect_delay">Reconnect delay (ms):</label>
                                <input id="noVNC_setting_reconnect_delay" type="number">
                            </li>
                            <li><hr></li>
                            <li>
                                <label>
                                    <input id="noVNC_setting_show_dot" type="checkbox"
                                           class="toggle">
                                    Show dot when no cursor
                                </label>
                            </li>
                            <li><hr></li>
                            <li>
                                <label>Logging:
                                    <select id="noVNC_setting_logging" name="vncLogging">
                                    </select>
                                </label>
                            </li>
                        </ul></div>
                    </li>
                    <li class="noVNC_version_separator"><hr></li>
                    <li class="noVNC_version_wrapper">
                        <span>Version:</span>
                        <span class="noVNC_version"></span>
                    </li>
                </ul>
            </div>
            </div>

            <input type="image" alt="Disconnect" src="{{ url_for('static', filename='novnc/app/images/disconnect.svg') }}"
                id="noVNC_disconnect_button" class="noVNC_button"
                title="Disconnect">

            </div>
        </div>

    </div>

    <div id="noVNC_hint_anchor" class="noVNC_vcenter">
        <div id="noVNC_control_bar_hint">
        </div>
    </div>

    <div id="noVNC_status"></div>

    <div class="noVNC_center">
        <div id="noVNC_connect_dlg">
            <p class="noVNC_logo" translate="no"><span>no</span>VNC</p>
            <div>
                <button id="noVNC_connect_button">
                    <img alt="" src="{{ url_for('static', filename='novnc/app/images/connect.svg') }}"> Connect
                </button>
            </div>
        </div>
    </div>

    <div class="noVNC_center noVNC_connect_layer">
    <div id="noVNC_verify_server_dlg" class="noVNC_panel"><form>
        <div class="noVNC_heading">
            Server identity
        </div>
        <div>
            The server has provided the following identifying information:
        </div>
        <div id="noVNC_fingerprint_block">
            Fingerprint:
            <span id="noVNC_fingerprint"></span>
        </div>
        <div>
            Please verify that the information is correct and press
            "Approve". Otherwise press "Reject".
        </div>
        <div class="button_row">
            <input id="noVNC_approve_server_button" type="submit" value="Approve">
            <input id="noVNC_reject_server_button" type="button" value="Reject">
        </div>
    </form></div>
    </div>

    <div class="noVNC_center noVNC_connect_layer">
    <div id="noVNC_credentials_dlg" class="noVNC_panel"><form>
        <div class="noVNC_heading">
            Credentials
        </div>
        <div id="noVNC_username_block">
            <label for="noVNC_username_input">Username:</label>
            <input id="noVNC_username_input">
        </div>
        <div id="noVNC_password_block">
            <label for="noVNC_password_input">Password:</label>
            <input id="noVNC_password_input" type="password">
        </div>
        <div class="button_row">
            <input id="noVNC_credentials_button" type="submit" value="Send credentials">
        </div>
    </form></div>
    </div>

    <div id="noVNC_transition">
        <div id="noVNC_transition_text"></div>
        <div>
        <input type="button" id="noVNC_cancel_reconnect_button" value="Cancel">
        </div>
        <div class="noVNC_spinner"></div>
    </div>

    <div id="noVNC_container">
        <textarea id="noVNC_keyboardinput" autocapitalize="off"
            autocomplete="off" spellcheck="false" tabindex="-1"></textarea>
    </div>

    <audio id="noVNC_bell">
        <source src="{{ url_for('static', filename='novnc/app/sounds/bell.oga') }}" type="audio/ogg">
        <source src="{{ url_for('static', filename='novnc/app/sounds/bell.mp3') }}" type="audio/mpeg">
    </audio>

    <script>
        // When VNC disconnects unexpectedly (e.g. iDRAC idle timeout kills the x11vnc
        // proxy), the noVNC "Connect" button would try to reuse the now-stale websockify
        // token.  Instead, call the session API first so the server can detect the dead
        // proxy and create a fresh x11vnc session, then navigate to the (possibly new)
        // console URL with autoconnect.
        //
        // When a new session is created the x11vnc proxy needs a few seconds to
        // complete its upstream connection to the iDRAC before noVNC can connect.
        // We delay the navigation for new sessions to avoid a "Failed to connect"
        // error on the first autoconnect attempt.
        document.getElementById('noVNC_connect_button').addEventListener('click', function(e) {
            var btn = this;
            e.stopImmediatePropagation();
            e.preventDefault();
            btn.disabled = true;
            btn.innerHTML = 'Connecting&hellip;';
            fetch('/api/vnc-session', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({hostname: riHostname})
            })
            .then(function(r) { return r.json(); })
            .then(function(data) {
                if (data.success) {
                    var newUrl = '/console/' + encodeURIComponent(data.token);
                    if (isSharedSession) newUrl += '?shared=1';
                    if (data.token !== consoleToken) {
                        // New session: release the stale token and give x11vnc
                        // time to finish its upstream connection before navigating.
                        fetch('/api/vnc-session/' + encodeURIComponent(consoleToken), {
                            method: 'DELETE',
                            keepalive: true
                        });
                        setTimeout(function() { window.location.replace(newUrl); }, 3500);
                    } else {
                        // Same session, proxy still alive: navigate immediately.
                        window.location.replace(newUrl);
                    }
                } else {
                    window.location.reload();
                }
            })
            .catch(function() {
                window.location.reload();
            });
        });
    </script>
</body>
</html>
