mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Refactor XML parse, use getElement() instead of overriding DOM.
This commit is contained in:
@@ -48,421 +48,35 @@ var Guacamole = Guacamole || {};
|
|||||||
*/
|
*/
|
||||||
Guacamole.OnScreenKeyboard = function(url) {
|
Guacamole.OnScreenKeyboard = function(url) {
|
||||||
|
|
||||||
var allKeys = new Array();
|
// For each child of element, call handler defined in next
|
||||||
var modifierState = new function() {};
|
function parseChildren(element, next) {
|
||||||
|
|
||||||
function getKeySize(size) {
|
var children = root.childNodes;
|
||||||
return (5*size) + "ex";
|
for (var i=0; i<children.length; i++) {
|
||||||
}
|
|
||||||
|
|
||||||
function getCapSize(size) {
|
// Get child node and corresponding handler
|
||||||
return (5*size - 0.5) + "ex";
|
var child = children[i];
|
||||||
}
|
var handler = next[child.tagName];
|
||||||
|
|
||||||
function clearModifiers() {
|
// Call handler if defined
|
||||||
|
if (handler)
|
||||||
|
handler(child);
|
||||||
|
|
||||||
// Send key release events for all pressed modifiers
|
// Throw exception if no handler
|
||||||
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;
|
|
||||||
if (!displayText) displayText = cap.text;
|
|
||||||
|
|
||||||
// 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 displayText;
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
else
|
||||||
press();
|
throw new Exception(
|
||||||
}
|
"Unexpected " + child.tagName
|
||||||
|
+ " within " + element.tagName
|
||||||
|
);
|
||||||
// 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
|
// Create keyboard
|
||||||
var keyboard = document.createElement("div");
|
var keyboard = document.createElement("div");
|
||||||
keyboard.className = "keyboard";
|
keyboard.className = "keyboard";
|
||||||
|
|
||||||
|
|
||||||
// Retrieve keyboard XML
|
// Retrieve keyboard XML
|
||||||
var xmlhttprequest = new XMLHttpRequest();
|
var xmlhttprequest = new XMLHttpRequest();
|
||||||
xmlhttprequest.open("GET", url, false);
|
xmlhttprequest.open("GET", url, false);
|
||||||
@@ -472,34 +86,107 @@ Guacamole.OnScreenKeyboard = function(url) {
|
|||||||
|
|
||||||
if (xml) {
|
if (xml) {
|
||||||
|
|
||||||
// Parse document
|
function parse_row(e) {
|
||||||
var root = xml.documentElement;
|
|
||||||
if (root) {
|
var row = document.createElement("div");
|
||||||
|
row.className = "row";
|
||||||
|
|
||||||
var children = root.childNodes;
|
parseChildren(e, {
|
||||||
for (var i=0; i<children.length; i++) {
|
|
||||||
var child = children[i];
|
"column": function(e) {
|
||||||
|
row.appendChild(parse_column(e));
|
||||||
|
},
|
||||||
|
|
||||||
|
"gap": function parse_gap(e) {
|
||||||
|
|
||||||
|
// Get attributes
|
||||||
|
var gap_size = e.attributes["size"];
|
||||||
|
|
||||||
|
// Create element
|
||||||
|
var gap = document.createElement("div");
|
||||||
|
gap.className = "gap";
|
||||||
|
gap.textContent = " ";
|
||||||
|
|
||||||
|
if (gap_size)
|
||||||
|
gap.style.width = gap.style.height =
|
||||||
|
parseFloat(gap_size.value) + "em";
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"key": function parse_key(e) {
|
||||||
|
|
||||||
|
// Get attributes
|
||||||
|
var key_size = e.attributes["size"];
|
||||||
|
|
||||||
|
parseChildren(e, {
|
||||||
|
"cap": function cap(e) {
|
||||||
|
|
||||||
|
// Get attributes
|
||||||
|
var required = e.attributes["if"];
|
||||||
|
var modifier = e.attributes["modifier"];
|
||||||
|
var keysym = e.attributes["keysym"];
|
||||||
|
var sticky = e.attributes["sticky"];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// <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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
return row;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parse_column(e) {
|
||||||
|
|
||||||
|
var col = document.createElement("div");
|
||||||
|
col.className = "col";
|
||||||
|
|
||||||
|
var align = col.attributes["align"];
|
||||||
|
|
||||||
|
if (align)
|
||||||
|
col.style.textAlign = align.value;
|
||||||
|
|
||||||
|
// Columns can only contain rows
|
||||||
|
parseChildren(e, {
|
||||||
|
"row": function(e) {
|
||||||
|
col.appendChild(parse_row(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return col;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse document
|
||||||
|
parseChildren(xml.documentElement, {
|
||||||
|
|
||||||
|
"keyboard": function parse_keyboard(e) {
|
||||||
|
|
||||||
|
// Get attributes
|
||||||
|
var keyboard_size = e.attributes["size"];
|
||||||
|
|
||||||
|
parseChildren(e, {
|
||||||
|
|
||||||
|
"row": function(e) {
|
||||||
|
keyboard.appendChild(parse_row(e));
|
||||||
|
},
|
||||||
|
|
||||||
|
"column": function(e) {
|
||||||
|
keyboard.appendChild(parse_column(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
} // end keyboard
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Do not allow selection or mouse movement to propagate/register.
|
||||||
keyboard.onselectstart =
|
keyboard.onselectstart =
|
||||||
keyboard.onmousemove =
|
keyboard.onmousemove =
|
||||||
@@ -510,7 +197,53 @@ Guacamole.OnScreenKeyboard = function(url) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return keyboard;
|
|
||||||
|
this.onkeypressed = null;
|
||||||
|
this.onkeyreleased = null;
|
||||||
|
|
||||||
|
this.getElement = function() {
|
||||||
|
return keyboard;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Guacamole.OnScreenKeyboard.Key = function() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of the key, relative to the size of the keyboard.
|
||||||
|
*/
|
||||||
|
this.size = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this key is currently pressed.
|
||||||
|
*/
|
||||||
|
this.pressed = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An associative map of all caps by modifier.
|
||||||
|
*/
|
||||||
|
this.caps = {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Guacamole.OnScreenKeyboard.Cap = function(text, keycode, modifier) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifier represented by this keycap
|
||||||
|
*/
|
||||||
|
this.modifier = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text to be displayed within this keycap
|
||||||
|
*/
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keycode this cap sends when its associated key is pressed/released
|
||||||
|
*/
|
||||||
|
this.keycode = keycode;
|
||||||
|
|
||||||
|
// Set modifier if provided
|
||||||
|
if (modifier) this.modifier = modifier;
|
||||||
|
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user