<commit_before>
// UI Definition
var GuacamoleUI = {

    "viewport"    : document.getElementById("viewportClone"),
    "display"     : document.getElementById("display"),
    "menu"        : document.getElementById("menu"),
    "menuControl" : document.getElementById("menuControl"),
    "touchMenu"   : document.getElementById("touchMenu"),
    "logo"        : document.getElementById("status-logo"),

    "buttons": {

        "showClipboard": document.getElementById("showClipboard"),
        "showKeyboard" : document.getElementById("showKeyboard"),
        "ctrlAltDelete": document.getElementById("ctrlAltDelete"),
        "reconnect"    : document.getElementById("reconnect"),
        "logout"       : document.getElementById("logout")

    },

    "containers": {
        "state"    : document.getElementById("statusDialog"),
        "clipboard": document.getElementById("clipboardDiv"),
        "keyboard" : document.getElementById("keyboardContainer")
    },
    
    "state"     : document.getElementById("statusText"),
    "clipboard" : document.getElementById("clipboard")

};

// Constant UI initialization and behavior
(function() {

    var menu_shaded = false;

    var shade_interval = null;
    var show_interval = null;

    // Cache error image (might not be available when error occurs)
    var guacErrorImage = new Image();
    guacErrorImage.src = "images/noguacamole-logo-24.png";

    // Function for adding a class to an element
    var addClass;

    // Function for removing a class from an element
    var removeClass;

    // If Node.classList is supported, implement addClass/removeClass using that
    if (Node.classList) {

        addClass = function(element, classname) {
            element.classList.add(classname);
        };
        
        removeClass = function(element, classname) {
            element.classList.remove(classname);
        };
        
    }

    // Otherwise, implement own
    else {

        addClass = function(element, classname) {

            // Simply add new class
            element.className += " " + classname;

        };
        
        removeClass = function(element, classname) {

            // Filter out classes with given name
            element.className = element.className.replace(/([^ ]+)[ ]*/g,
                function(match, testClassname, spaces, offset, string) {

                    // If same class, remove
                    if (testClassname == classname)
                        return "";

                    // Otherwise, allow
                    return match;
                    
                }
            );

        };
        
    }


    GuacamoleUI.hideStatus = function() {
        removeClass(document.body, "guac-error");
        GuacamoleUI.containers.state.style.visibility = "hidden";
    };
    
    GuacamoleUI.showStatus = function(text) {
        removeClass(document.body, "guac-error");
        GuacamoleUI.containers.state.style.visibility = "visible";
        GuacamoleUI.state.textContent = text;
    };
    
    GuacamoleUI.showError = function(error) {
        addClass(document.body, "guac-error");
        GuacamoleUI.state.textContent = error;
    };

    GuacamoleUI.shadeMenu = function() {

        if (!menu_shaded) {

            var step = Math.floor(GuacamoleUI.menu.offsetHeight / 10) + 1;
            var offset = 0;
            menu_shaded = true;

            window.clearInterval(show_interval);
            shade_interval = window.setInterval(function() {

                offset -= step;
                GuacamoleUI.menu.style.top = offset + "px";

                if (offset <= -GuacamoleUI.menu.offsetHeight) {
                    window.clearInterval(shade_interval);
                    GuacamoleUI.menu.style.visiblity = "hidden";
                }

            }, 30);
        }

    };

    GuacamoleUI.showMenu = function() {

        if (menu_shaded) {

            var step = Math.floor(GuacamoleUI.menu.offsetHeight / 5) + 1;
            var offset = -GuacamoleUI.menu.offsetHeight;
            menu_shaded = false;
            GuacamoleUI.menu.style.visiblity = "";

            window.clearInterval(shade_interval);
            show_interval = window.setInterval(function() {

                offset += step;

                if (offset >= 0) {
                    offset = 0;
                    window.clearInterval(show_interval);
                }

                GuacamoleUI.menu.style.top = offset + "px";

            }, 30);
        }

    };

    // Show/Hide clipboard
    GuacamoleUI.buttons.showClipboard.onclick = function() {

        var displayed = GuacamoleUI.containers.clipboard.style.display;
        if (displayed != "block") {
            GuacamoleUI.containers.clipboard.style.display = "block";
            GuacamoleUI.buttons.showClipboard.innerHTML = "Hide Clipboard";
        }
        else {
            GuacamoleUI.containers.clipboard.style.display = "none";
            GuacamoleUI.buttons.showClipboard.innerHTML = "Show Clipboard";
            GuacamoleUI.clipboard.onchange();
        }

    };

    // Show/Hide keyboard
    var keyboardResizeInterval = null;
    GuacamoleUI.buttons.showKeyboard.onclick = function() {

        var displayed = GuacamoleUI.containers.keyboard.style.display;
        if (displayed != "block") {
            GuacamoleUI.containers.keyboard.style.display = "block";
            GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";

            // Automatically update size
            window.onresize = updateKeyboardSize;
            keyboardResizeInterval = window.setInterval(updateKeyboardSize, 30);

            updateKeyboardSize();
        }
        else {
            GuacamoleUI.containers.keyboard.style.display = "none";
            GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";

            window.onresize = null;
            window.clearInterval(keyboardResizeInterval);
        }

    };

    // Logout
    GuacamoleUI.buttons.logout.onclick = function() {
        window.location.href = "logout";
    };

    // Timeouts for detecting if users wants menu to open or close
    var detectMenuOpenTimeout = null;
    var detectMenuCloseTimeout = null;

    // Clear detection timeouts
    GuacamoleUI.resetMenuDetect = function() {

        if (detectMenuOpenTimeout != null) {
            window.clearTimeout(detectMenuOpenTimeout);
            detectMenuOpenTimeout = null;
        }

        if (detectMenuCloseTimeout != null) {
            window.clearTimeout(detectMenuCloseTimeout);
            detectMenuCloseTimeout = null;
        }

    };

    // Initiate detection of menu open action. If not canceled through some
    // user event, menu will open.
    GuacamoleUI.startMenuOpenDetect = function() {

        if (!detectMenuOpenTimeout) {

            // Clear detection state
            GuacamoleUI.resetMenuDetect();

            // Wait and then show menu
            detectMenuOpenTimeout = window.setTimeout(function() {
                GuacamoleUI.showMenu();
                detectMenuOpenTimeout = null;
            }, 325);

        }

    };

    // Initiate detection of menu close action. If not canceled through some
    // user event, menu will close.
    GuacamoleUI.startMenuCloseDetect = function() {

        if (!detectMenuCloseTimeout) {

            // Clear detection state
            GuacamoleUI.resetMenuDetect();

            // Wait and then shade menu
            detectMenuCloseTimeout = window.setTimeout(function() {
                GuacamoleUI.shadeMenu();
                detectMenuCloseTimeout = null;
            }, 500);

        }

    };

    // Show menu if mouseover any part of menu
    GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.showMenu, true);

    // Stop detecting menu state change intents if mouse is over menu
    GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.resetMenuDetect, true);

    // When mouse hovers over top of screen, start detection of intent to open menu
    GuacamoleUI.menuControl.addEventListener('mousemove', GuacamoleUI.startMenuOpenDetect, true);

    var menuShowLongPressTimeout = null;

    GuacamoleUI.startLongPressDetect = function() {

        if (!menuShowLongPressTimeout) {

            menuShowLongPressTimeout = window.setTimeout(function() {
                
                menuShowLongPressTimeout = null;
                GuacamoleUI.showMenu();

            }, 800);

        }
    };

    GuacamoleUI.stopLongPressDetect = function() {
        window.clearTimeout(menuShowLongPressTimeout);
        menuShowLongPressTimeout = null;
    };

    // Detect long-press at bottom of screen
    document.body.addEventListener('touchstart', GuacamoleUI.startLongPressDetect, true);

    // Reconnect button
    GuacamoleUI.buttons.reconnect.onclick = function() {
        window.location.reload();
    };

    // On-screen keyboard
    GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty-mobile.xml");
    GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard.getElement());

    // Function for automatically updating keyboard size
    var lastKeyboardWidth;
    function updateKeyboardSize() {
        var currentSize = GuacamoleUI.keyboard.getElement().offsetWidth;
        if (lastKeyboardWidth != currentSize) {
            GuacamoleUI.keyboard.resize(currentSize);
            lastKeyboardWidth = currentSize;
        }
    };

})();

// Tie UI events / behavior to a specific Guacamole client
GuacamoleUI.attach = function(guac) {

    var guac_display = guac.getDisplay();

    // When mouse enters display, start detection of intent to close menu
    guac_display.addEventListener('mouseover', GuacamoleUI.startMenuCloseDetect, true);

    guac_display.onclick = function(e) {
        e.preventDefault();
        return false;
    };

    // Mouse
    var mouse = new Guacamole.Mouse(guac_display);
    mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
        function(mouseState) {
       
            // Determine mouse position within view
            var mouse_view_x = mouseState.x + guac_display.offsetLeft - window.pageXOffset;
            var mouse_view_y = mouseState.y + guac_display.offsetTop  - window.pageYOffset;

            // Determine viewport dimensioins
            var view_width  = GuacamoleUI.viewport.offsetWidth;
            var view_height = GuacamoleUI.viewport.offsetHeight;

            // Determine scroll amounts based on mouse position relative to document

            var scroll_amount_x;
            if (mouse_view_x > view_width)
                scroll_amount_x = mouse_view_x - view_width;
            else if (mouse_view_x < 0)
                scroll_amount_x = mouse_view_x;
            else
                scroll_amount_x = 0;

            var scroll_amount_y;
            if (mouse_view_y > view_height)
                scroll_amount_y = mouse_view_y - view_height;
            else if (mouse_view_y < 0)
                scroll_amount_y = mouse_view_y;
            else
                scroll_amount_y = 0;

            // Scroll (if necessary) to keep mouse on screen.
            window.scrollBy(scroll_amount_x, scroll_amount_y);
       
            // Hide menu on movement
            GuacamoleUI.startMenuCloseDetect();

            // Stop detecting long presses if mouse is being used
            GuacamoleUI.stopLongPressDetect();

            // Send mouse event
            guac.sendMouseState(mouseState);
            
        };

    // Keyboard
    var keyboard = new Guacamole.Keyboard(document);

    function disableKeyboard() {
        keyboard.onkeydown = null;
        keyboard.onkeyup = null;
    }

    function enableKeyboard() {
        keyboard.onkeydown = 
            function (keysym) {
                guac.sendKeyEvent(1, keysym);
            };

        keyboard.onkeyup = 
            function (keysym) {
                guac.sendKeyEvent(0, keysym);
            };
    }

    // Enable keyboard by default
    enableKeyboard();

    // Handle client state change
    guac.onstatechange = function(clientState) {
        switch (clientState) {

            // Idle
            case 0:
                GuacamoleUI.showStatus("Idle.");
                break;

            // Connecting
            case 1:
                GuacamoleUI.shadeMenu();
                GuacamoleUI.showStatus("Connecting...");
                break;

            // Connected + waiting
            case 2:
                GuacamoleUI.showStatus("Connected, waiting for first update...");
                break;

            // Connected
            case 3:
                
                GuacamoleUI.hideStatus();
                GuacamoleUI.display.className =
                    GuacamoleUI.display.className.replace(/guac-loading/, '');

                GuacamoleUI.menu.className = "connected";
                break;

            // Disconnecting
            case 4:
                GuacamoleUI.showStatus("Disconnecting...");
                break;

            // Disconnected
            case 5:
                GuacamoleUI.showStatus("Disconnected.");
                break;

            // Unknown status code
            default:
                GuacamoleUI.showStatus("[UNKNOWN STATUS]");

        }
    };

    // Name instruction handler
    guac.onname = function(name) {
        document.title = name;
    };

    // Error handler
    guac.onerror = function(error) {

        // Disconnect, if connected
        guac.disconnect();

        // Display error message
        GuacamoleUI.showError(error);
        
    };

    // Disconnect on close
    window.onunload = function() {
        guac.disconnect();
    };

    // Handle clipboard events
    GuacamoleUI.clipboard.onchange = function() {

        var text = GuacamoleUI.clipboard.value;
        guac.setClipboard(text);

    };

    // Ignore keypresses when clipboard is focused
    GuacamoleUI.clipboard.onfocus = function() {
        disableKeyboard();
    };

    // Capture keypresses when clipboard is not focused
    GuacamoleUI.clipboard.onblur = function() {
        enableKeyboard();
    };

    // Server copy handler
    guac.onclipboard = function(data) {
        GuacamoleUI.clipboard.value = data;
    };

    GuacamoleUI.keyboard.onkeydown = function(keysym) {
        guac.sendKeyEvent(1, keysym);
    };

    GuacamoleUI.keyboard.onkeyup = function(keysym) {
        guac.sendKeyEvent(0, keysym);
    };

    // Send Ctrl-Alt-Delete
    GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {

        var KEYSYM_CTRL   = 0xFFE3;
        var KEYSYM_ALT    = 0xFFE9;
        var KEYSYM_DELETE = 0xFFFF;

        guac.sendKeyEvent(1, KEYSYM_CTRL);
        guac.sendKeyEvent(1, KEYSYM_ALT);
        guac.sendKeyEvent(1, KEYSYM_DELETE);
        guac.sendKeyEvent(0, KEYSYM_DELETE);
        guac.sendKeyEvent(0, KEYSYM_ALT);
        guac.sendKeyEvent(0, KEYSYM_CTRL);
    };

};<commit_msg>Fade out display on error.<commit_after>
// UI Definition
var GuacamoleUI = {

    "viewport"    : document.getElementById("viewportClone"),
    "display"     : document.getElementById("display"),
    "menu"        : document.getElementById("menu"),
    "menuControl" : document.getElementById("menuControl"),
    "touchMenu"   : document.getElementById("touchMenu"),
    "logo"        : document.getElementById("status-logo"),

    "buttons": {

        "showClipboard": document.getElementById("showClipboard"),
        "showKeyboard" : document.getElementById("showKeyboard"),
        "ctrlAltDelete": document.getElementById("ctrlAltDelete"),
        "reconnect"    : document.getElementById("reconnect"),
        "logout"       : document.getElementById("logout")

    },

    "containers": {
        "state"    : document.getElementById("statusDialog"),
        "clipboard": document.getElementById("clipboardDiv"),
        "keyboard" : document.getElementById("keyboardContainer")
    },
    
    "state"     : document.getElementById("statusText"),
    "clipboard" : document.getElementById("clipboard")

};

// Constant UI initialization and behavior
(function() {

    var menu_shaded = false;

    var shade_interval = null;
    var show_interval = null;

    // Cache error image (might not be available when error occurs)
    var guacErrorImage = new Image();
    guacErrorImage.src = "images/noguacamole-logo-24.png";

    // Function for adding a class to an element
    var addClass;

    // Function for removing a class from an element
    var removeClass;

    // If Node.classList is supported, implement addClass/removeClass using that
    if (Node.classList) {

        addClass = function(element, classname) {
            element.classList.add(classname);
        };
        
        removeClass = function(element, classname) {
            element.classList.remove(classname);
        };
        
    }

    // Otherwise, implement own
    else {

        addClass = function(element, classname) {

            // Simply add new class
            element.className += " " + classname;

        };
        
        removeClass = function(element, classname) {

            // Filter out classes with given name
            element.className = element.className.replace(/([^ ]+)[ ]*/g,
                function(match, testClassname, spaces, offset, string) {

                    // If same class, remove
                    if (testClassname == classname)
                        return "";

                    // Otherwise, allow
                    return match;
                    
                }
            );

        };
        
    }


    GuacamoleUI.hideStatus = function() {
        removeClass(document.body, "guac-error");
        GuacamoleUI.containers.state.style.visibility = "hidden";
        GuacamoleUI.display.style.opacity = "1";
    };
    
    GuacamoleUI.showStatus = function(text) {
        removeClass(document.body, "guac-error");
        GuacamoleUI.containers.state.style.visibility = "visible";
        GuacamoleUI.state.textContent = text;
        GuacamoleUI.display.style.opacity = "1";
    };
    
    GuacamoleUI.showError = function(error) {
        addClass(document.body, "guac-error");
        GuacamoleUI.state.textContent = error;
        GuacamoleUI.display.style.opacity = "0.1";
    };

    GuacamoleUI.shadeMenu = function() {

        if (!menu_shaded) {

            var step = Math.floor(GuacamoleUI.menu.offsetHeight / 10) + 1;
            var offset = 0;
            menu_shaded = true;

            window.clearInterval(show_interval);
            shade_interval = window.setInterval(function() {

                offset -= step;
                GuacamoleUI.menu.style.top = offset + "px";

                if (offset <= -GuacamoleUI.menu.offsetHeight) {
                    window.clearInterval(shade_interval);
                    GuacamoleUI.menu.style.visiblity = "hidden";
                }

            }, 30);
        }

    };

    GuacamoleUI.showMenu = function() {

        if (menu_shaded) {

            var step = Math.floor(GuacamoleUI.menu.offsetHeight / 5) + 1;
            var offset = -GuacamoleUI.menu.offsetHeight;
            menu_shaded = false;
            GuacamoleUI.menu.style.visiblity = "";

            window.clearInterval(shade_interval);
            show_interval = window.setInterval(function() {

                offset += step;

                if (offset >= 0) {
                    offset = 0;
                    window.clearInterval(show_interval);
                }

                GuacamoleUI.menu.style.top = offset + "px";

            }, 30);
        }

    };

    // Show/Hide clipboard
    GuacamoleUI.buttons.showClipboard.onclick = function() {

        var displayed = GuacamoleUI.containers.clipboard.style.display;
        if (displayed != "block") {
            GuacamoleUI.containers.clipboard.style.display = "block";
            GuacamoleUI.buttons.showClipboard.innerHTML = "Hide Clipboard";
        }
        else {
            GuacamoleUI.containers.clipboard.style.display = "none";
            GuacamoleUI.buttons.showClipboard.innerHTML = "Show Clipboard";
            GuacamoleUI.clipboard.onchange();
        }

    };

    // Show/Hide keyboard
    var keyboardResizeInterval = null;
    GuacamoleUI.buttons.showKeyboard.onclick = function() {

        var displayed = GuacamoleUI.containers.keyboard.style.display;
        if (displayed != "block") {
            GuacamoleUI.containers.keyboard.style.display = "block";
            GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";

            // Automatically update size
            window.onresize = updateKeyboardSize;
            keyboardResizeInterval = window.setInterval(updateKeyboardSize, 30);

            updateKeyboardSize();
        }
        else {
            GuacamoleUI.containers.keyboard.style.display = "none";
            GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";

            window.onresize = null;
            window.clearInterval(keyboardResizeInterval);
        }

    };

    // Logout
    GuacamoleUI.buttons.logout.onclick = function() {
        window.location.href = "logout";
    };

    // Timeouts for detecting if users wants menu to open or close
    var detectMenuOpenTimeout = null;
    var detectMenuCloseTimeout = null;

    // Clear detection timeouts
    GuacamoleUI.resetMenuDetect = function() {

        if (detectMenuOpenTimeout != null) {
            window.clearTimeout(detectMenuOpenTimeout);
            detectMenuOpenTimeout = null;
        }

        if (detectMenuCloseTimeout != null) {
            window.clearTimeout(detectMenuCloseTimeout);
            detectMenuCloseTimeout = null;
        }

    };

    // Initiate detection of menu open action. If not canceled through some
    // user event, menu will open.
    GuacamoleUI.startMenuOpenDetect = function() {

        if (!detectMenuOpenTimeout) {

            // Clear detection state
            GuacamoleUI.resetMenuDetect();

            // Wait and then show menu
            detectMenuOpenTimeout = window.setTimeout(function() {
                GuacamoleUI.showMenu();
                detectMenuOpenTimeout = null;
            }, 325);

        }

    };

    // Initiate detection of menu close action. If not canceled through some
    // user event, menu will close.
    GuacamoleUI.startMenuCloseDetect = function() {

        if (!detectMenuCloseTimeout) {

            // Clear detection state
            GuacamoleUI.resetMenuDetect();

            // Wait and then shade menu
            detectMenuCloseTimeout = window.setTimeout(function() {
                GuacamoleUI.shadeMenu();
                detectMenuCloseTimeout = null;
            }, 500);

        }

    };

    // Show menu if mouseover any part of menu
    GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.showMenu, true);

    // Stop detecting menu state change intents if mouse is over menu
    GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.resetMenuDetect, true);

    // When mouse hovers over top of screen, start detection of intent to open menu
    GuacamoleUI.menuControl.addEventListener('mousemove', GuacamoleUI.startMenuOpenDetect, true);

    var menuShowLongPressTimeout = null;

    GuacamoleUI.startLongPressDetect = function() {

        if (!menuShowLongPressTimeout) {

            menuShowLongPressTimeout = window.setTimeout(function() {
                
                menuShowLongPressTimeout = null;
                GuacamoleUI.showMenu();

            }, 800);

        }
    };

    GuacamoleUI.stopLongPressDetect = function() {
        window.clearTimeout(menuShowLongPressTimeout);
        menuShowLongPressTimeout = null;
    };

    // Detect long-press at bottom of screen
    document.body.addEventListener('touchstart', GuacamoleUI.startLongPressDetect, true);

    // Reconnect button
    GuacamoleUI.buttons.reconnect.onclick = function() {
        window.location.reload();
    };

    // On-screen keyboard
    GuacamoleUI.keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty-mobile.xml");
    GuacamoleUI.containers.keyboard.appendChild(GuacamoleUI.keyboard.getElement());

    // Function for automatically updating keyboard size
    var lastKeyboardWidth;
    function updateKeyboardSize() {
        var currentSize = GuacamoleUI.keyboard.getElement().offsetWidth;
        if (lastKeyboardWidth != currentSize) {
            GuacamoleUI.keyboard.resize(currentSize);
            lastKeyboardWidth = currentSize;
        }
    };

})();

// Tie UI events / behavior to a specific Guacamole client
GuacamoleUI.attach = function(guac) {

    var guac_display = guac.getDisplay();

    // When mouse enters display, start detection of intent to close menu
    guac_display.addEventListener('mouseover', GuacamoleUI.startMenuCloseDetect, true);

    guac_display.onclick = function(e) {
        e.preventDefault();
        return false;
    };

    // Mouse
    var mouse = new Guacamole.Mouse(guac_display);
    mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
        function(mouseState) {
       
            // Determine mouse position within view
            var mouse_view_x = mouseState.x + guac_display.offsetLeft - window.pageXOffset;
            var mouse_view_y = mouseState.y + guac_display.offsetTop  - window.pageYOffset;

            // Determine viewport dimensioins
            var view_width  = GuacamoleUI.viewport.offsetWidth;
            var view_height = GuacamoleUI.viewport.offsetHeight;

            // Determine scroll amounts based on mouse position relative to document

            var scroll_amount_x;
            if (mouse_view_x > view_width)
                scroll_amount_x = mouse_view_x - view_width;
            else if (mouse_view_x < 0)
                scroll_amount_x = mouse_view_x;
            else
                scroll_amount_x = 0;

            var scroll_amount_y;
            if (mouse_view_y > view_height)
                scroll_amount_y = mouse_view_y - view_height;
            else if (mouse_view_y < 0)
                scroll_amount_y = mouse_view_y;
            else
                scroll_amount_y = 0;

            // Scroll (if necessary) to keep mouse on screen.
            window.scrollBy(scroll_amount_x, scroll_amount_y);
       
            // Hide menu on movement
            GuacamoleUI.startMenuCloseDetect();

            // Stop detecting long presses if mouse is being used
            GuacamoleUI.stopLongPressDetect();

            // Send mouse event
            guac.sendMouseState(mouseState);
            
        };

    // Keyboard
    var keyboard = new Guacamole.Keyboard(document);

    function disableKeyboard() {
        keyboard.onkeydown = null;
        keyboard.onkeyup = null;
    }

    function enableKeyboard() {
        keyboard.onkeydown = 
            function (keysym) {
                guac.sendKeyEvent(1, keysym);
            };

        keyboard.onkeyup = 
            function (keysym) {
                guac.sendKeyEvent(0, keysym);
            };
    }

    // Enable keyboard by default
    enableKeyboard();

    // Handle client state change
    guac.onstatechange = function(clientState) {
        switch (clientState) {

            // Idle
            case 0:
                GuacamoleUI.showStatus("Idle.");
                break;

            // Connecting
            case 1:
                GuacamoleUI.shadeMenu();
                GuacamoleUI.showStatus("Connecting...");
                break;

            // Connected + waiting
            case 2:
                GuacamoleUI.showStatus("Connected, waiting for first update...");
                break;

            // Connected
            case 3:
                
                GuacamoleUI.hideStatus();
                GuacamoleUI.display.className =
                    GuacamoleUI.display.className.replace(/guac-loading/, '');

                GuacamoleUI.menu.className = "connected";
                break;

            // Disconnecting
            case 4:
                GuacamoleUI.showStatus("Disconnecting...");
                break;

            // Disconnected
            case 5:
                GuacamoleUI.showStatus("Disconnected.");
                break;

            // Unknown status code
            default:
                GuacamoleUI.showStatus("[UNKNOWN STATUS]");

        }
    };

    // Name instruction handler
    guac.onname = function(name) {
        document.title = name;
    };

    // Error handler
    guac.onerror = function(error) {

        // Disconnect, if connected
        guac.disconnect();

        // Display error message
        GuacamoleUI.showError(error);
        
    };

    // Disconnect on close
    window.onunload = function() {
        guac.disconnect();
    };

    // Handle clipboard events
    GuacamoleUI.clipboard.onchange = function() {

        var text = GuacamoleUI.clipboard.value;
        guac.setClipboard(text);

    };

    // Ignore keypresses when clipboard is focused
    GuacamoleUI.clipboard.onfocus = function() {
        disableKeyboard();
    };

    // Capture keypresses when clipboard is not focused
    GuacamoleUI.clipboard.onblur = function() {
        enableKeyboard();
    };

    // Server copy handler
    guac.onclipboard = function(data) {
        GuacamoleUI.clipboard.value = data;
    };

    GuacamoleUI.keyboard.onkeydown = function(keysym) {
        guac.sendKeyEvent(1, keysym);
    };

    GuacamoleUI.keyboard.onkeyup = function(keysym) {
        guac.sendKeyEvent(0, keysym);
    };

    // Send Ctrl-Alt-Delete
    GuacamoleUI.buttons.ctrlAltDelete.onclick = function() {

        var KEYSYM_CTRL   = 0xFFE3;
        var KEYSYM_ALT    = 0xFFE9;
        var KEYSYM_DELETE = 0xFFFF;

        guac.sendKeyEvent(1, KEYSYM_CTRL);
        guac.sendKeyEvent(1, KEYSYM_ALT);
        guac.sendKeyEvent(1, KEYSYM_DELETE);
        guac.sendKeyEvent(0, KEYSYM_DELETE);
        guac.sendKeyEvent(0, KEYSYM_ALT);
        guac.sendKeyEvent(0, KEYSYM_CTRL);
    };

};
{'fraction_non_alphanumeric': 0.09750348582431445, 'fraction_numerical': 0.0034526259876502224, 'mean_word_length': 1.9408376452211267, 'pattern_counts': {'":': 10, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4211876', 'n_tokens_mistral': 8621, 'n_tokens_neox': 8198, 'n_words': 1628}