mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
488 lines
12 KiB
JavaScript
488 lines
12 KiB
JavaScript
|
|
/*
|
|
* Guacamole - Pure JavaScript/HTML VNC Client
|
|
* Copyright (C) 2010 Michael Jumper
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
function GuacamoleOnScreenKeyboard(url) {
|
|
|
|
var tabIndex = 1;
|
|
var allKeys = new Array();
|
|
var modifierState = new function() {};
|
|
|
|
function getKeySize(size) {
|
|
return (5*size) + "ex";
|
|
}
|
|
|
|
function getCapSize(size) {
|
|
return (5*size - 0.5) + "ex";
|
|
}
|
|
|
|
function clearModifiers() {
|
|
|
|
// Send key release events for all pressed modifiers
|
|
for (var k=0; k<allKeys.length; k++) {
|
|
|
|
var key = allKeys[k];
|
|
var cap = key.getCap();
|
|
var modifier = cap.getModifier();
|
|
|
|
if (modifier && isModifierActive(modifier) && !cap.isSticky() && key.isPressed())
|
|
key.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function setModifierReleased(modifier) {
|
|
if (isModifierActive(modifier))
|
|
modifierState[modifier]--;
|
|
}
|
|
|
|
function setModifierPressed(modifier) {
|
|
if (modifierState[modifier] == null)
|
|
modifierState[modifier] = 1;
|
|
else
|
|
modifierState[modifier]++;
|
|
}
|
|
|
|
function isModifierActive(modifier) {
|
|
if (modifierState[modifier] > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function toggleModifierPressed(modifier) {
|
|
if (isModifierActive(modifier))
|
|
setModifierReleased(modifier);
|
|
else
|
|
setModifierPressed(modifier);
|
|
}
|
|
|
|
function refreshAllKeysState() {
|
|
for (var k=0; k<allKeys.length; k++)
|
|
allKeys[k].refreshState();
|
|
}
|
|
|
|
function Key(key) {
|
|
|
|
function Cap(cap) {
|
|
|
|
// Displayed text
|
|
var displayText = cap.textContent;
|
|
|
|
// Keysym
|
|
var keysym = null;
|
|
if (cap.attributes["keysym"])
|
|
keysym = parseInt(cap.attributes["keysym"].value);
|
|
|
|
// If keysym not specified, get keysym from display text.
|
|
else if (displayText.length == 1) {
|
|
|
|
var charCode = displayText.charCodeAt(0);
|
|
|
|
if (charCode >= 0x0000 && charCode <= 0x00FF)
|
|
keysym = charCode;
|
|
|
|
else if (charCode >= 0x0100 && charCode <= 0x10FFFF)
|
|
keysym = 0x01000000 | charCode;
|
|
}
|
|
|
|
// Required modifiers for this keycap
|
|
var reqMod = null;
|
|
if (cap.attributes["if"])
|
|
reqMod = cap.attributes["if"].value.split(",");
|
|
|
|
|
|
// Modifier represented by this keycap
|
|
var modifier = null;
|
|
if (cap.attributes["modifier"])
|
|
modifier = cap.attributes["modifier"].value;
|
|
|
|
|
|
// Whether this key is sticky (toggles)
|
|
// Currently only valid for modifiers.
|
|
var sticky = false;
|
|
if (cap.attributes["sticky"] && cap.attributes["sticky"].value == "true")
|
|
sticky = true;
|
|
|
|
this.getDisplayText = function() {
|
|
return cap.textContent;
|
|
};
|
|
|
|
this.getKeySym = function() {
|
|
return keysym;
|
|
};
|
|
|
|
this.getRequiredModifiers = function() {
|
|
return reqMod;
|
|
};
|
|
|
|
this.getModifier = function() {
|
|
return modifier;
|
|
};
|
|
|
|
this.isSticky = function() {
|
|
return sticky;
|
|
};
|
|
|
|
}
|
|
|
|
var size = null;
|
|
if (key.attributes["size"])
|
|
size = parseFloat(key.attributes["size"].value);
|
|
|
|
var caps = key.getElementsByTagName("cap");
|
|
var keycaps = new Array();
|
|
for (var i=0; i<caps.length; i++)
|
|
keycaps.push(new Cap(caps[i]));
|
|
|
|
var rowKey = document.createElement("div");
|
|
rowKey.className = "key";
|
|
|
|
var keyCap = document.createElement("div");
|
|
keyCap.className = "cap";
|
|
rowKey.appendChild(keyCap);
|
|
|
|
|
|
var STATE_RELEASED = 0;
|
|
var STATE_PRESSED = 1;
|
|
var state = STATE_RELEASED;
|
|
|
|
rowKey.isPressed = function() {
|
|
return state == STATE_PRESSED;
|
|
}
|
|
|
|
var currentCap = null;
|
|
function refreshState(modifier) {
|
|
|
|
// Find current cap
|
|
currentCap = null;
|
|
for (var j=0; j<keycaps.length; j++) {
|
|
|
|
var keycap = keycaps[j];
|
|
var required = keycap.getRequiredModifiers();
|
|
|
|
var matches = true;
|
|
|
|
// If modifiers required, make sure all modifiers are active
|
|
if (required) {
|
|
|
|
for (var k=0; k<required.length; k++) {
|
|
if (!isModifierActive(required[k])) {
|
|
matches = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (matches)
|
|
currentCap = keycap;
|
|
|
|
}
|
|
|
|
rowKey.className = "key";
|
|
|
|
if (currentCap.getModifier())
|
|
rowKey.className += " modifier";
|
|
|
|
if (currentCap.isSticky())
|
|
rowKey.className += " sticky";
|
|
|
|
if (isModifierActive(currentCap.getModifier()))
|
|
rowKey.className += " active";
|
|
|
|
if (state == STATE_PRESSED)
|
|
rowKey.className += " pressed";
|
|
|
|
keyCap.textContent = currentCap.getDisplayText();
|
|
}
|
|
rowKey.refreshState = refreshState;
|
|
|
|
rowKey.getCap = function() {
|
|
return currentCap;
|
|
};
|
|
|
|
refreshState();
|
|
|
|
// Set size
|
|
if (size) {
|
|
rowKey.style.width = getKeySize(size);
|
|
keyCap.style.width = getCapSize(size);
|
|
}
|
|
|
|
|
|
|
|
// Set pressed, if released
|
|
function press() {
|
|
|
|
if (state == STATE_RELEASED) {
|
|
|
|
state = STATE_PRESSED;
|
|
|
|
var keysym = currentCap.getKeySym();
|
|
var modifier = currentCap.getModifier();
|
|
var sticky = currentCap.isSticky();
|
|
|
|
if (keyPressedHandler && keysym)
|
|
keyPressedHandler(keysym);
|
|
|
|
if (modifier) {
|
|
|
|
// If sticky modifier, toggle
|
|
if (sticky)
|
|
toggleModifierPressed(modifier);
|
|
|
|
// Otherwise, just set on.
|
|
else
|
|
setModifierPressed(modifier);
|
|
|
|
refreshAllKeysState();
|
|
}
|
|
else
|
|
refreshState();
|
|
}
|
|
|
|
}
|
|
rowKey.press = press;
|
|
|
|
|
|
// Set released, if pressed
|
|
function release() {
|
|
|
|
if (state == STATE_PRESSED) {
|
|
|
|
state = STATE_RELEASED;
|
|
|
|
var keysym = currentCap.getKeySym();
|
|
var modifier = currentCap.getModifier();
|
|
var sticky = currentCap.isSticky();
|
|
|
|
if (keyReleasedHandler && keysym)
|
|
keyReleasedHandler(keysym);
|
|
|
|
if (modifier) {
|
|
|
|
// If not sticky modifier, release modifier
|
|
if (!sticky) {
|
|
setModifierReleased(modifier);
|
|
refreshAllKeysState();
|
|
}
|
|
else
|
|
refreshState();
|
|
|
|
}
|
|
else {
|
|
refreshState();
|
|
|
|
// If not a modifier, also release all pressed modifiers
|
|
clearModifiers();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
rowKey.release = release;
|
|
|
|
// Toggle press/release states
|
|
function toggle() {
|
|
if (state == STATE_PRESSED)
|
|
release();
|
|
else
|
|
press();
|
|
}
|
|
|
|
|
|
// Send key press on mousedown
|
|
rowKey.onmousedown = function(e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
var modifier = currentCap.getModifier();
|
|
var sticky = currentCap.isSticky();
|
|
|
|
// Toggle non-sticky modifiers
|
|
if (modifier && !sticky)
|
|
toggle();
|
|
|
|
// Press all others
|
|
else
|
|
press();
|
|
|
|
return false;
|
|
};
|
|
|
|
// Send key release on mouseup/out
|
|
rowKey.onmouseout =
|
|
rowKey.onmouseout =
|
|
rowKey.onmouseup = function(e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
var modifier = currentCap.getModifier();
|
|
var sticky = currentCap.isSticky();
|
|
|
|
// Release non-modifiers and sticky modifiers
|
|
if (!modifier || sticky)
|
|
release();
|
|
|
|
return false;
|
|
};
|
|
|
|
rowKey.onselectstart = function() { return false; };
|
|
|
|
return rowKey;
|
|
|
|
}
|
|
|
|
function Gap(gap) {
|
|
|
|
var keyboardGap = document.createElement("div");
|
|
keyboardGap.className = "gap";
|
|
keyboardGap.textContent = " ";
|
|
|
|
var size = null;
|
|
if (gap.attributes["size"])
|
|
size = parseFloat(gap.attributes["size"].value);
|
|
|
|
if (size) {
|
|
keyboardGap.style.width = getKeySize(size);
|
|
keyboardGap.style.height = getKeySize(size);
|
|
}
|
|
|
|
return keyboardGap;
|
|
|
|
}
|
|
|
|
function Row(row) {
|
|
|
|
var keyboardRow = document.createElement("div");
|
|
keyboardRow.className = "row";
|
|
|
|
var children = row.childNodes;
|
|
for (var j=0; j<children.length; j++) {
|
|
var child = children[j];
|
|
|
|
// <row> can contain <key> or <column>
|
|
if (child.tagName == "key") {
|
|
var key = new Key(child);
|
|
keyboardRow.appendChild(key);
|
|
allKeys.push(key);
|
|
}
|
|
else if (child.tagName == "gap") {
|
|
var gap = new Gap(child);
|
|
keyboardRow.appendChild(gap);
|
|
}
|
|
else if (child.tagName == "column") {
|
|
var col = new Column(child);
|
|
keyboardRow.appendChild(col);
|
|
}
|
|
|
|
}
|
|
|
|
return keyboardRow;
|
|
|
|
}
|
|
|
|
function Column(col) {
|
|
|
|
var keyboardCol = document.createElement("div");
|
|
keyboardCol.className = "col";
|
|
|
|
var align = null;
|
|
if (col.attributes["align"])
|
|
align = col.attributes["align"].value;
|
|
|
|
var children = col.childNodes;
|
|
for (var j=0; j<children.length; j++) {
|
|
var child = children[j];
|
|
|
|
// <column> can only contain <row>
|
|
if (child.tagName == "row") {
|
|
var row = new Row(child);
|
|
keyboardCol.appendChild(row);
|
|
}
|
|
|
|
}
|
|
|
|
if (align)
|
|
keyboardCol.style.textAlign = align;
|
|
|
|
return keyboardCol;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create keyboard
|
|
var keyboard = document.createElement("div");
|
|
keyboard.className = "keyboard";
|
|
|
|
|
|
// Retrieve keyboard XML
|
|
var xmlhttprequest = new XMLHttpRequest();
|
|
xmlhttprequest.open("GET", url, false);
|
|
xmlhttprequest.send(null);
|
|
|
|
var xml = xmlhttprequest.responseXML;
|
|
|
|
if (xml) {
|
|
|
|
// Parse document
|
|
var root = xml.documentElement;
|
|
if (root) {
|
|
|
|
var children = root.childNodes;
|
|
for (var i=0; i<children.length; i++) {
|
|
var child = children[i];
|
|
|
|
// <keyboard> can contain <row> or <column>
|
|
if (child.tagName == "row") {
|
|
keyboard.appendChild(new Row(child));
|
|
}
|
|
else if (child.tagName == "column") {
|
|
keyboard.appendChild(new Column(child));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var keyPressedHandler = null;
|
|
var keyReleasedHandler = null;
|
|
|
|
keyboard.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
|
|
keyboard.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };
|
|
|
|
// Do not allow selection or mouse movement to propagate/register.
|
|
keyboard.onselectstart =
|
|
keyboard.onmousemove =
|
|
keyboard.onmouseup =
|
|
keyboard.onmousedown =
|
|
function(e) {
|
|
e.stopPropagation();
|
|
return false;
|
|
};
|
|
|
|
return keyboard;
|
|
}
|
|
|