mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
687 lines
20 KiB
JavaScript
687 lines
20 KiB
JavaScript
|
|
// UI Definition
|
|
var GuacamoleUI = {
|
|
|
|
/* Detection Constants */
|
|
|
|
"LONG_PRESS_DETECT_TIMEOUT" : 800, /* milliseconds */
|
|
"LONG_PRESS_MOVEMENT_THRESHOLD" : 10, /* pixels */
|
|
"MENU_CLOSE_DETECT_TIMEOUT" : 500, /* milliseconds */
|
|
"MENU_OPEN_DETECT_TIMEOUT" : 325, /* milliseconds */
|
|
"KEYBOARD_AUTO_RESIZE_INTERVAL" : 30, /* milliseconds */
|
|
|
|
/* Animation Constants */
|
|
|
|
"MENU_SHADE_STEPS" : 10, /* frames */
|
|
"MENU_SHADE_INTERVAL" : 30, /* milliseconds */
|
|
"MENU_SHOW_STEPS" : 5, /* frames */
|
|
"MENU_SHOW_INTERVAL" : 30, /* milliseconds */
|
|
|
|
/* OSK Mode Constants */
|
|
"OSK_MODE_NATIVE" : 1, /* "Show Keyboard" will show the platform's native OSK */
|
|
"OSK_MODE_GUAC" : 2, /* "Show Keyboard" will show Guac's built-in OSK */
|
|
|
|
/* UI Elements */
|
|
|
|
"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"),
|
|
"eventTarget" : document.getElementById("eventTarget"),
|
|
|
|
"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 / GuacamoleUI.MENU_SHADE_STEPS) + 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";
|
|
}
|
|
|
|
}, GuacamoleUI.MENU_SHADE_INTERVAL);
|
|
}
|
|
|
|
};
|
|
|
|
GuacamoleUI.showMenu = function() {
|
|
|
|
if (menu_shaded) {
|
|
|
|
var step = Math.floor(GuacamoleUI.menu.offsetHeight / GuacamoleUI.MENU_SHOW_STEPS) + 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";
|
|
|
|
}, GuacamoleUI.MENU_SHOW_INTERVAL);
|
|
}
|
|
|
|
};
|
|
|
|
// 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();
|
|
}
|
|
|
|
};
|
|
|
|
// Assume no native OSK by default
|
|
GuacamoleUI.oskMode = GuacamoleUI.OSK_MODE_GUAC;
|
|
|
|
// Show/Hide keyboard
|
|
var keyboardResizeInterval = null;
|
|
GuacamoleUI.buttons.showKeyboard.onclick = function() {
|
|
|
|
// If Guac OSK shown, hide it.
|
|
var displayed = GuacamoleUI.containers.keyboard.style.display;
|
|
if (displayed == "block") {
|
|
GuacamoleUI.containers.keyboard.style.display = "none";
|
|
GuacamoleUI.buttons.showKeyboard.textContent = "Show Keyboard";
|
|
|
|
window.onresize = null;
|
|
window.clearInterval(keyboardResizeInterval);
|
|
}
|
|
|
|
// If not shown ... action depends on OSK mode.
|
|
else {
|
|
|
|
// If we think the platform has a native OSK, use the event target to
|
|
// cause it to display.
|
|
if (GuacamoleUI.oskMode == GuacamoleUI.OSK_MODE_NATIVE) {
|
|
|
|
// ...but use the Guac OSK if clicked again
|
|
GuacamoleUI.oskMode = GuacamoleUI.OSK_MODE_GUAC;
|
|
|
|
// Try to show native OSK by focusing eventTarget.
|
|
GuacamoleUI.eventTarget.focus();
|
|
return;
|
|
|
|
}
|
|
|
|
// Ensure event target is NOT focused if we are using the Guac OSK.
|
|
GuacamoleUI.eventTarget.blur();
|
|
|
|
GuacamoleUI.containers.keyboard.style.display = "block";
|
|
GuacamoleUI.buttons.showKeyboard.textContent = "Hide Keyboard";
|
|
|
|
// Automatically update size
|
|
window.onresize = updateKeyboardSize;
|
|
keyboardResizeInterval = window.setInterval(updateKeyboardSize, GuacamoleUI.KEYBOARD_AUTO_RESIZE_INTERVAL);
|
|
|
|
updateKeyboardSize();
|
|
}
|
|
|
|
|
|
};
|
|
|
|
// 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() {
|
|
|
|
// If menu opened via mouse, do not show native OSK
|
|
GuacamoleUI.oskMode = GuacamoleUI.OSK_MODE_GUAC;
|
|
|
|
GuacamoleUI.showMenu();
|
|
detectMenuOpenTimeout = null;
|
|
}, GuacamoleUI.MENU_OPEN_DETECT_TIMEOUT);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Initiate detection of menu close action. If not canceled through some
|
|
// user mouse 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;
|
|
}, GuacamoleUI.MENU_CLOSE_DETECT_TIMEOUT);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// 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 long_press_start_x = 0;
|
|
var long_press_start_y = 0;
|
|
var menuShowLongPressTimeout = null;
|
|
|
|
GuacamoleUI.startLongPressDetect = function() {
|
|
|
|
if (!menuShowLongPressTimeout) {
|
|
|
|
menuShowLongPressTimeout = window.setTimeout(function() {
|
|
|
|
menuShowLongPressTimeout = null;
|
|
|
|
// Assume native OSK if menu shown via long-press
|
|
GuacamoleUI.oskMode = GuacamoleUI.OSK_MODE_NATIVE;
|
|
GuacamoleUI.showMenu();
|
|
|
|
}, GuacamoleUI.LONG_PRESS_DETECT_TIMEOUT);
|
|
|
|
}
|
|
};
|
|
|
|
GuacamoleUI.stopLongPressDetect = function() {
|
|
window.clearTimeout(menuShowLongPressTimeout);
|
|
menuShowLongPressTimeout = null;
|
|
};
|
|
|
|
// Detect long-press at bottom of screen
|
|
GuacamoleUI.display.addEventListener('touchstart', function(e) {
|
|
|
|
// Close menu if shown
|
|
GuacamoleUI.shadeMenu();
|
|
|
|
// Record touch location
|
|
if (e.touches.length == 1) {
|
|
var touch = e.touches[0];
|
|
long_press_start_x = touch.screenX;
|
|
long_press_start_y = touch.screenY;
|
|
}
|
|
|
|
// Start detection
|
|
GuacamoleUI.startLongPressDetect();
|
|
|
|
}, true);
|
|
|
|
// Stop detection if touch moves significantly
|
|
GuacamoleUI.display.addEventListener('touchmove', function(e) {
|
|
|
|
if (e.touches.length == 1) {
|
|
|
|
// If touch distance from start exceeds threshold, cancel long press
|
|
var touch = e.touches[0];
|
|
if (Math.abs(touch.screenX - long_press_start_x) >= GuacamoleUI.LONG_PRESS_MOVEMENT_THRESHOLD
|
|
|| Math.abs(touch.screenY - long_press_start_y) >= GuacamoleUI.LONG_PRESS_MOVEMENT_THRESHOLD)
|
|
GuacamoleUI.stopLongPressDetect();
|
|
|
|
}
|
|
|
|
}, true);
|
|
|
|
// Stop detection if press stops
|
|
GuacamoleUI.display.addEventListener('touchend', GuacamoleUI.stopLongPressDetect, true);
|
|
|
|
// Close menu on mouse movement
|
|
GuacamoleUI.display.addEventListener('mousemove', GuacamoleUI.startMenuCloseDetect, true);
|
|
GuacamoleUI.display.addEventListener('mousedown', GuacamoleUI.startMenuCloseDetect, 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 title_prefix = null;
|
|
var connection_name = "Guacamole";
|
|
|
|
var guac_display = guac.getDisplay();
|
|
|
|
// Set document title appropriately, based on prefix and connection name
|
|
function updateTitle() {
|
|
|
|
// Use title prefix if present
|
|
if (title_prefix) {
|
|
|
|
document.title = title_prefix;
|
|
|
|
// Include connection name, if present
|
|
if (connection_name)
|
|
document.title += " " + connection_name;
|
|
|
|
}
|
|
|
|
// Otherwise, just set to connection name
|
|
else if (connection_name)
|
|
document.title = connection_name;
|
|
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Send mouse event
|
|
guac.sendMouseState(mouseState);
|
|
|
|
};
|
|
|
|
// Keyboard
|
|
var keyboard = new Guacamole.Keyboard(document);
|
|
var keysymPressed = [];
|
|
|
|
function isTypableCharacter(keysym) {
|
|
return (keysym & 0xFFFF00) != 0xFF00;
|
|
}
|
|
|
|
function disableKeyboard() {
|
|
keyboard.onkeydown = null;
|
|
keyboard.onkeyup = null;
|
|
}
|
|
|
|
function enableKeyboard() {
|
|
|
|
keyboard.onkeydown = function (keysym) {
|
|
guac.sendKeyEvent(1, keysym);
|
|
keysymPressed[keysym] = true;
|
|
return isTypableCharacter(keysym);
|
|
};
|
|
|
|
keyboard.onkeyup = function (keysym) {
|
|
guac.sendKeyEvent(0, keysym);
|
|
keysymPressed[keysym] = false;
|
|
return isTypableCharacter(keysym);
|
|
};
|
|
|
|
}
|
|
|
|
// Enable keyboard by default
|
|
enableKeyboard();
|
|
|
|
// Handle client state change
|
|
guac.onstatechange = function(clientState) {
|
|
|
|
switch (clientState) {
|
|
|
|
// Idle
|
|
case 0:
|
|
GuacamoleUI.showStatus("Idle.");
|
|
title_prefix = "[Idle]";
|
|
break;
|
|
|
|
// Connecting
|
|
case 1:
|
|
GuacamoleUI.shadeMenu();
|
|
GuacamoleUI.showStatus("Connecting...");
|
|
title_prefix = "[Connecting...]";
|
|
break;
|
|
|
|
// Connected + waiting
|
|
case 2:
|
|
GuacamoleUI.showStatus("Connected, waiting for first update...");
|
|
title_prefix = "[Waiting...]";
|
|
break;
|
|
|
|
// Connected
|
|
case 3:
|
|
GuacamoleUI.hideStatus();
|
|
title_prefix = null;
|
|
break;
|
|
|
|
// Disconnecting
|
|
case 4:
|
|
GuacamoleUI.showStatus("Disconnecting...");
|
|
title_prefix = "[Disconnecting...]";
|
|
break;
|
|
|
|
// Disconnected
|
|
case 5:
|
|
GuacamoleUI.showStatus("Disconnected.");
|
|
title_prefix = "[Disconnected]";
|
|
break;
|
|
|
|
// Unknown status code
|
|
default:
|
|
GuacamoleUI.showStatus("[UNKNOWN STATUS]");
|
|
|
|
}
|
|
|
|
updateTitle();
|
|
};
|
|
|
|
// Name instruction handler
|
|
guac.onname = function(name) {
|
|
connection_name = name;
|
|
updateTitle();
|
|
};
|
|
|
|
// 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();
|
|
};
|
|
|
|
// Save length for calculation of changed value
|
|
var currentLength = GuacamoleUI.eventTarget.value.length;
|
|
|
|
// If text is input directly into event target without typing (as with
|
|
// voice input, for example), type automatically.
|
|
GuacamoleUI.eventTarget.oninput = function(e) {
|
|
|
|
// Calculate current length and change in length
|
|
var newLength = GuacamoleUI.eventTarget.value.length;
|
|
var changeLength = newLength - currentLength;
|
|
currentLength = newLength;
|
|
|
|
// If deleted or replaced text, ignore
|
|
if (changeLength <= 0)
|
|
return;
|
|
|
|
// Get changed text
|
|
var text = GuacamoleUI.eventTarget.value.substring(
|
|
GuacamoleUI.eventTarget.selectionStart,
|
|
GuacamoleUI.eventTarget.selectionStart + changeLength
|
|
);
|
|
|
|
// Send each character
|
|
for (var i=0; i<text.length; i++) {
|
|
|
|
// Get char code
|
|
var charCode = text.charCodeAt(i);
|
|
|
|
// Convert to keysym
|
|
var keysym = 0x003F; // Default to a question mark
|
|
if (charCode >= 0x0000 && charCode <= 0x00FF)
|
|
keysym = charCode;
|
|
else if (charCode >= 0x0100 && charCode <= 0x10FFFF)
|
|
keysym = 0x01000000 | charCode;
|
|
|
|
// Send keysym only if not already pressed
|
|
if (!keysymPressed[keysym]) {
|
|
|
|
// Press and release key
|
|
guac.sendKeyEvent(1, keysym);
|
|
guac.sendKeyEvent(0, keysym);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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);
|
|
};
|
|
|
|
}; |