Updated to Guacamole 0.9.1, now closes the session correctly
@ -7,7 +7,7 @@
|
||||
<groupId>org.openuds.server</groupId>
|
||||
<artifactId>transport</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>1.2.1</version>
|
||||
<version>1.5.0</version>
|
||||
<name>Guacamole Transport</name>
|
||||
<url>http://openuds.org/</url>
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
<dependency>
|
||||
<groupId>org.glyptodon.guacamole</groupId>
|
||||
<artifactId>guacamole-common-js</artifactId>
|
||||
<version>0.9.0</version>
|
||||
<version>0.9.1</version>
|
||||
<type>zip</type>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
@ -78,7 +78,7 @@ public class TunnelServlet
|
||||
throw new GuacamoleException("Can't access required user credentials");
|
||||
}
|
||||
|
||||
System.out.println("Got parameters from remote server");
|
||||
System.out.println("Got parameters from remote server: " + data + ", " + width + "x" + height);
|
||||
|
||||
GuacamoleClientInformation info = new GuacamoleClientInformation();
|
||||
info.setOptimalScreenWidth(Integer.parseInt(width));
|
||||
|
After Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 512 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 611 B |
After Width: | Height: | Size: 690 B |
After Width: | Height: | Size: 525 B |
After Width: | Height: | Size: 707 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 560 B |
After Width: | Height: | Size: 626 B |
After Width: | Height: | Size: 648 B |
After Width: | Height: | Size: 810 B |
After Width: | Height: | Size: 843 B |
After Width: | Height: | Size: 717 B |
BIN
guacamole-tunnel/src/main/webapp/images/guac-mono-192.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/guacamole-logo-144.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/guacamole-logo-24.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 326 B |
BIN
guacamole-tunnel/src/main/webapp/images/noguacamole-logo-24.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 691 B |
After Width: | Height: | Size: 727 B |
After Width: | Height: | Size: 792 B |
BIN
guacamole-tunnel/src/main/webapp/images/settings/tablet-keys.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/settings/touchpad.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/settings/touchscreen.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/settings/zoom-in.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/settings/zoom-out.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
guacamole-tunnel/src/main/webapp/images/user-icons/guac-user.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
@ -1,44 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE HTML>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!--
|
||||
Guacamole - Clientless Remote Desktop
|
||||
Copyright (C) 2010 Michael Jumper
|
||||
Copyright (C) 2013 Glyptodon LLC
|
||||
|
||||
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.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
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.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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/>.
|
||||
|
||||
Adaptation for UDS use (c) 2013 Virtual Cable S.L.U, also distributed under
|
||||
GNU Affero General Public License
|
||||
|
||||
All source code used to build this package can be obtained from http://openuds.org
|
||||
Guacamole source code can be obtained from http://guac-dev.org/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
|
||||
<link rel="stylesheet" type="text/css" href="styles/ui.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="styles/client.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi"/>
|
||||
<link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=medium-dpi"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
|
||||
<title>UDS</title>
|
||||
|
||||
<title>Guacamole ${project.version}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<!-- Display -->
|
||||
<div class="displayOuter">
|
||||
<div class="displayMiddle">
|
||||
@ -47,11 +47,92 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Text input target -->
|
||||
<div id="text-input"><div id="text-input-field"><div id="sent-history"></div><textarea rows="1" id="target"></textarea></div><div id="text-input-buttons"><button class="key" data-keysym="0xFFE3" data-sticky="true">Ctrl</button><button class="key" data-keysym="0xFFE9" data-sticky="true">Alt</button><button class="key" data-keysym="0xFF1B">Esc</button><button class="key" data-keysym="0xFF09">Tab</button></div></div>
|
||||
|
||||
<!-- Dimensional clone of viewport -->
|
||||
<div id="viewportClone"/>
|
||||
|
||||
<!-- Notification area -->
|
||||
<div id="notificationArea"/>
|
||||
|
||||
<!-- Menu -->
|
||||
<div id="menu">
|
||||
<h2 id="menu-title">Guacamole ${project.version}</h2>
|
||||
|
||||
<h3>Clipboard</h3>
|
||||
<div class="content" id="clipboard-settings">
|
||||
<p class="description">Text copied/cut within Guacamole will appear here. Changes to the text below will affect the remote clipboard.</p>
|
||||
<textarea rows="10" cols="40" id="clipboard"></textarea>
|
||||
</div>
|
||||
|
||||
<h3>Input method</h3>
|
||||
<div class="content" id="keyboard-settings">
|
||||
|
||||
<!-- No IME -->
|
||||
<div class="choice">
|
||||
<label><input name="input-method" type="radio" value="ime-none" checked="checked" id="ime-none"/> None</label>
|
||||
<p class="caption"><label for="ime-none">No input method is used. Keyboard input is accepted from
|
||||
a connected, physical keyboard.</label></p>
|
||||
</div>
|
||||
|
||||
<!-- Text input -->
|
||||
<div class="choice">
|
||||
<div class="figure"><label for="ime-text"><img src="images/settings/tablet-keys.png" alt=""/></label></div>
|
||||
<label><input name="input-method" type="radio" value="ime-text" id="ime-text"/> Text input</label>
|
||||
<p class="caption"><label for="ime-text">
|
||||
Allow typing of text, and emulate keyboard events based on the
|
||||
typed text. This is necessary for devices such as mobile phones that lack a physical keyboard.</label></p>
|
||||
</div>
|
||||
|
||||
<!-- Guac OSK -->
|
||||
<div class="choice">
|
||||
<label><input name="input-method" type="radio" value="ime-osk" id="ime-osk"/> On-screen keyboard</label>
|
||||
<p class="caption"><label for="ime-osk">Display and accept input from the built-in Guacamole on-screen
|
||||
keyboard. The on-screen keyboard allows typing of key combinations that may otherwise be impossible
|
||||
(such as Ctrl-Alt-Del).</label></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3>Mouse emulation mode</h3>
|
||||
<div class="content" id="mouse-settings">
|
||||
<p class="description">Determines how the remote mouse behaves with respect to touches.</p>
|
||||
|
||||
<!-- Touchscreen -->
|
||||
<div class="choice">
|
||||
<input name="mouse-mode" type="radio" value="absolute" checked="checked" id="absolute"/>
|
||||
<div class="figure">
|
||||
<label for="absolute"><img src="images/settings/touchscreen.png" alt=""/></label>
|
||||
<p class="caption"><label for="absolute">Tap to click. The click occurs at the location of the touch.</label></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Touchpad -->
|
||||
<div class="choice">
|
||||
<input name="mouse-mode" type="radio" value="relative" id="relative"/>
|
||||
<div class="figure">
|
||||
<label for="relative"><img src="images/settings/touchpad.png" alt=""/></label>
|
||||
<p class="caption"><label for="relative">Drag to move the mouse pointer and tap to click. The click occurs at the location of the pointer.</label></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3>Display</h3>
|
||||
<div class="content">
|
||||
<div id="zoom-settings">
|
||||
<div id="zoom-out"><img src="images/settings/zoom-out.png" alt="-"/></div>
|
||||
<div id="zoom-state">100%</div>
|
||||
<div id="zoom-in"><img src="images/settings/zoom-in.png" alt="+"/></div>
|
||||
</div>
|
||||
<div><label><input type="checkbox" id="auto-fit" checked="checked"/> Automatically fit to browser window</label></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Images which should be preloaded -->
|
||||
<div id="preload">
|
||||
<img src="images/action-icons/guac-close.png"/>
|
||||
@ -61,98 +142,46 @@
|
||||
<script type="text/javascript" src="scripts/lib/blob/blob.js"></script>
|
||||
<script type="text/javascript" src="scripts/lib/filesaver/filesaver.js"></script>
|
||||
|
||||
<!-- Client core scripts -->
|
||||
<!-- guacamole-common-js -->
|
||||
<script type="text/javascript" src="guacamole-common-js/all.min.js"></script>
|
||||
|
||||
|
||||
<!-- guacamole-default-webapp scripts -->
|
||||
<script type="text/javascript" src="scripts/session.js"></script>
|
||||
<script type="text/javascript" src="scripts/history.js"></script>
|
||||
<script type="text/javascript" src="scripts/service.js"></script>
|
||||
<script type="text/javascript" src="scripts/guac-ui.js"></script>
|
||||
<script type="text/javascript" src="scripts/client-ui.js"></script>
|
||||
|
||||
<!-- Init -->
|
||||
<script type="text/javascript"> /* <![CDATA[ */
|
||||
|
||||
function getQueryParams(qs) {
|
||||
qs = qs.split("+").join(" ");
|
||||
|
||||
var params = {}, tokens,
|
||||
re = /[?&]?([^=]+)=([^&]*)/g;
|
||||
|
||||
while (tokens = re.exec(qs)) {
|
||||
params[decodeURIComponent(tokens[1])]
|
||||
= decodeURIComponent(tokens[2]);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
window.query = getQueryParams(document.location.search);
|
||||
|
||||
// Sanity check Guacamole JavaScript API version
|
||||
// if (Guacamole.API_VERSION !== "0.9.1")
|
||||
// GuacUI.Client.showStatus("Clear Your Cache", "An older version of Guacamole has been cached by your browser. Please clear your browser's cache and try again.");
|
||||
|
||||
// Start connect after control returns from onload (allow browser
|
||||
// to consider the page loaded).
|
||||
//else
|
||||
window.onload = function() {
|
||||
window.setTimeout(function() {
|
||||
// Get parameter information, so we can retrieve connecton data
|
||||
var params = window.location.search.substr(1);
|
||||
var data = "";
|
||||
var exitUrl = "";
|
||||
var ioa = params.indexOf('&');
|
||||
// URL is in the format ?XXXXXXXX&URL, where XXX is the id of the credential stored, and URL is the output URL
|
||||
if( ioa != -1 ) {
|
||||
data = params.substr(0,ioa);
|
||||
exiturl = params.substr(ioa+1);
|
||||
} else {
|
||||
data = params;
|
||||
}
|
||||
|
||||
var tunnel;
|
||||
|
||||
GuacUI.sessionState.setProperty("auto-fit", true);
|
||||
GuacUI.sessionState.setProperty("exitUrl", exiturl);
|
||||
GuacUI.sessionState.setProperty("disable-sound", false);
|
||||
|
||||
// If WebSocket available, try to use it.
|
||||
/*if (window.WebSocket)
|
||||
tunnel = new Guacamole.ChainedTunnel(
|
||||
new Guacamole.WebSocketTunnel("websocket-tunnel"),
|
||||
new Guacamole.HTTPTunnel("tunnel")
|
||||
);
|
||||
|
||||
// If no WebSocket, then use HTTP.
|
||||
else*/
|
||||
tunnel = new Guacamole.HTTPTunnel("tunnel")
|
||||
|
||||
// Instantiate client
|
||||
var guac = new Guacamole.Client(tunnel);
|
||||
|
||||
// Add client to UI
|
||||
guac.getDisplay().className = "software-cursor";
|
||||
GuacUI.Client.display.appendChild(guac.getDisplay());
|
||||
|
||||
// Tie UI to client
|
||||
GuacUI.Client.attach(guac);
|
||||
|
||||
try {
|
||||
|
||||
// Calculate optimal width/height for display
|
||||
var optimal_width = window.innerWidth;
|
||||
var optimal_height = window.innerHeight;
|
||||
|
||||
// Scale width/height to be at least 600x600
|
||||
if (optimal_width < 600 || optimal_height < 600) {
|
||||
var scale = Math.max(600 / optimal_width, 600 / optimal_height);
|
||||
optimal_width = Math.floor(optimal_width * scale);
|
||||
optimal_height = Math.floor(optimal_height * scale);
|
||||
}
|
||||
|
||||
// Get entire query string, and pass to connect().
|
||||
// Normally, only the "id" parameter is required, but
|
||||
// all parameters should be preserved and passed on for
|
||||
// the sake of authentication.
|
||||
|
||||
var connect_string = "data=" + data + "&width=" + optimal_width + "&height=" + optimal_height;
|
||||
|
||||
// Add audio mimetypes to connect_string
|
||||
GuacUI.Audio.supported.forEach(function(mimetype) {
|
||||
connect_string += "&audio=" + encodeURIComponent(mimetype);
|
||||
});
|
||||
|
||||
// Add video mimetypes to connect_string
|
||||
GuacUI.Video.supported.forEach(function(mimetype) {
|
||||
connect_string += "&video=" + encodeURIComponent(mimetype);
|
||||
});
|
||||
|
||||
guac.connect(connect_string);
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
GuacUI.Client.showError(e.message);
|
||||
}
|
||||
|
||||
}, 0);
|
||||
window.setTimeout(GuacUI.Client.connect, 10);
|
||||
};
|
||||
|
||||
/* ]]> */ </script>
|
||||
|
||||
</body>
|
||||
|
@ -1,19 +1,23 @@
|
||||
/*
|
||||
* Guacamole - Clientless Remote Desktop
|
||||
* Copyright (C) 2010 Michael Jumper
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -76,7 +80,6 @@ GuacAdmin.Field = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simple HTML input field.
|
||||
*
|
||||
@ -108,7 +111,6 @@ GuacAdmin.Field._HTML_INPUT = function(type) {
|
||||
|
||||
GuacAdmin.Field._HTML_INPUT.prototype = new GuacAdmin.Field();
|
||||
|
||||
|
||||
/**
|
||||
* A basic text field.
|
||||
*
|
||||
@ -120,6 +122,34 @@ GuacAdmin.Field.TEXT = function() {
|
||||
|
||||
GuacAdmin.Field.TEXT.prototype = new GuacAdmin.Field._HTML_INPUT();
|
||||
|
||||
/**
|
||||
* A basic multiline text field.
|
||||
*
|
||||
* @augments GuacAdmin.Field
|
||||
*/
|
||||
GuacAdmin.Field.MULTILINE = function() {
|
||||
|
||||
// Call parent constructor
|
||||
GuacAdmin.Field.apply(this);
|
||||
|
||||
// Create backing element
|
||||
var element = GuacUI.createElement("textarea");
|
||||
|
||||
this.getValue = function() {
|
||||
return element.value;
|
||||
};
|
||||
|
||||
this.getElement = function() {
|
||||
return element;
|
||||
};
|
||||
|
||||
this.setValue = function(value) {
|
||||
element.value = value;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
GuacAdmin.Field.MULTILINE.prototype = new GuacAdmin.Field();
|
||||
|
||||
/**
|
||||
* A basic password field.
|
||||
@ -132,7 +162,6 @@ GuacAdmin.Field.PASSWORD = function() {
|
||||
|
||||
GuacAdmin.Field.PASSWORD.prototype = new GuacAdmin.Field._HTML_INPUT();
|
||||
|
||||
|
||||
/**
|
||||
* A basic numeric field, leveraging the new HTML5 field types.
|
||||
*
|
||||
@ -144,7 +173,6 @@ GuacAdmin.Field.NUMERIC = function() {
|
||||
|
||||
GuacAdmin.Field.NUMERIC.prototype = new GuacAdmin.Field._HTML_INPUT();
|
||||
|
||||
|
||||
/**
|
||||
* Simple checkbox.
|
||||
*
|
||||
@ -216,7 +244,6 @@ GuacAdmin.Field.ENUM = function(values) {
|
||||
|
||||
GuacAdmin.Field.ENUM.prototype = new GuacAdmin.Field();
|
||||
|
||||
|
||||
/**
|
||||
* An arbitrary button.
|
||||
*
|
||||
@ -363,7 +390,6 @@ GuacAdmin.addUser = function(name, parameters) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* User edit dialog which allows editing of the user's password and connection
|
||||
* access level.
|
||||
@ -619,8 +645,8 @@ GuacAdmin.UserEditor = function(name, parameters) {
|
||||
GuacAdmin.reset();
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
};
|
||||
@ -658,8 +684,8 @@ GuacAdmin.UserEditor = function(name, parameters) {
|
||||
}
|
||||
|
||||
// Alert on failure
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -816,8 +842,10 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
||||
start.textContent = GuacAdmin.formatDate(record.start);
|
||||
if (record.duration !== null)
|
||||
duration.textContent = GuacAdmin.formatSeconds(record.duration);
|
||||
else
|
||||
else if (record.active)
|
||||
duration.textContent = "Active now";
|
||||
else
|
||||
duration.textContent = "-";
|
||||
|
||||
// Add record to pager
|
||||
history_pager.addElement(row);
|
||||
@ -882,6 +910,11 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
||||
field = new GuacAdmin.Field.ENUM(parameter.options);
|
||||
break;
|
||||
|
||||
// Multiline text field
|
||||
case GuacamoleService.Protocol.Parameter.MULTILINE:
|
||||
field = new GuacAdmin.Field.MULTILINE();
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
|
||||
@ -952,8 +985,8 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
||||
GuacAdmin.reset();
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
};
|
||||
@ -991,8 +1024,8 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
||||
}
|
||||
|
||||
// Alert on failure
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1145,8 +1178,8 @@ GuacAdmin.ConnectionGroupEditor = function(group, parameters) {
|
||||
GuacAdmin.reset();
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
};
|
||||
@ -1184,8 +1217,8 @@ GuacAdmin.ConnectionGroupEditor = function(group, parameters) {
|
||||
}
|
||||
|
||||
// Alert on failure
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(status.message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1375,8 +1408,8 @@ GuacAdmin.reset = function() {
|
||||
}
|
||||
|
||||
// Alert on failure
|
||||
catch (e) {
|
||||
alert(e.message);
|
||||
catch (status) {
|
||||
alert(tatusmessage);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,19 +1,23 @@
|
||||
/*
|
||||
* Guacamole - Clientless Remote Desktop
|
||||
* Copyright (C) 2010 Michael Jumper
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -22,11 +26,6 @@
|
||||
*/
|
||||
var GuacUI = GuacUI || {};
|
||||
|
||||
/**
|
||||
* Current session state, including settings.
|
||||
*/
|
||||
GuacUI.sessionState = new GuacamoleSessionState();
|
||||
|
||||
/**
|
||||
* Creates a new element having the given tagname and CSS class.
|
||||
*/
|
||||
@ -108,6 +107,55 @@ GuacUI.removeClass = function(element, classname) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the connection group having the given ID in a new tab/window.
|
||||
*
|
||||
* @param {String} id The ID of the connection group to open.
|
||||
* @param {String} parameters Any parameters that should be added to the URL,
|
||||
* for sake of authentication.
|
||||
*/
|
||||
GuacUI.openConnectionGroup = function(id, parameters) {
|
||||
GuacUI.openObject("g/" + id, parameters);
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the connection having the given ID in a new tab/window.
|
||||
*
|
||||
* @param {String} id The ID of the connection to open.
|
||||
* @param {String} parameters Any parameters that should be added to the URL,
|
||||
* for sake of authentication.
|
||||
*/
|
||||
GuacUI.openConnection = function(id, parameters) {
|
||||
GuacUI.openObject("c/" + id, parameters);
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the object having the given ID in a new tab/window. The ID must
|
||||
* include the relevant prefix.
|
||||
*
|
||||
* @param {String} id The ID of the object to open, including prefix.
|
||||
* @param {String} parameters Any parameters that should be added to the URL,
|
||||
* for sake of authentication.
|
||||
*/
|
||||
GuacUI.openObject = function(id, parameters) {
|
||||
|
||||
// Get URL
|
||||
var url = "client.xhtml?id=" + encodeURIComponent(id);
|
||||
|
||||
// Add parameters, if given
|
||||
if (parameters)
|
||||
url += "&" + parameters;
|
||||
|
||||
// Attempt to focus existing window
|
||||
var current = window.open(null, id);
|
||||
|
||||
// If window did not already exist, set up as
|
||||
// Guacamole client
|
||||
if (!current.GuacUI)
|
||||
window.open(url, id);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Object describing the UI's level of audio support. If the user has request
|
||||
* that audio be disabled, this object will pretend that audio is not
|
||||
@ -133,7 +181,7 @@ GuacUI.Audio = new (function() {
|
||||
this.supported = [];
|
||||
|
||||
// If sound disabled, we're done now.
|
||||
if (GuacUI.sessionState.getProperty("disable-sound"))
|
||||
if (GuacamoleSessionStorage.getItem("disable-sound", false))
|
||||
return;
|
||||
|
||||
// Build array of supported audio formats
|
||||
@ -214,203 +262,6 @@ GuacUI.Video = new (function() {
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* Central registry of all components for all states.
|
||||
*/
|
||||
GuacUI.StateManager = new (function() {
|
||||
|
||||
/**
|
||||
* The current state.
|
||||
*/
|
||||
var current_state = null;
|
||||
|
||||
/**
|
||||
* Array of arrays of components, indexed by the states they are in.
|
||||
*/
|
||||
var components = [];
|
||||
|
||||
/**
|
||||
* Registers the given component with this state manager, to be shown
|
||||
* during the given states.
|
||||
*
|
||||
* @param {GuacUI.Component} component The component to register.
|
||||
* @param {Number} [...] The list of states this component should be
|
||||
* visible during.
|
||||
*/
|
||||
this.registerComponent = function(component) {
|
||||
|
||||
// For each state specified, add the given component
|
||||
for (var i=1; i<arguments.length; i++) {
|
||||
|
||||
// Get specified state
|
||||
var state = arguments[i];
|
||||
|
||||
// Get array of components in that state
|
||||
var component_array = components[state];
|
||||
if (!component_array)
|
||||
component_array = components[state] = [];
|
||||
|
||||
// Add component
|
||||
component_array.push(component);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function allComponents(components, name) {
|
||||
|
||||
// Invoke given function on all components in array
|
||||
for (var i=0; i<components.length; i++)
|
||||
components[i][name]();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current visible state.
|
||||
*/
|
||||
this.setState = function(state) {
|
||||
|
||||
// Hide components in current state
|
||||
if (current_state && components[current_state])
|
||||
allComponents(components[current_state], "hide");
|
||||
|
||||
// Show all components in new state
|
||||
current_state = state;
|
||||
if (components[state])
|
||||
allComponents(components[state], "show");
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current visible state.
|
||||
*/
|
||||
this.getState = function() {
|
||||
return current_state;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* Abstract component which can be registered with GuacUI and shown or hidden
|
||||
* dynamically based on interface mode.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
GuacUI.Component = function() {
|
||||
|
||||
/**
|
||||
* Called whenever this component needs to be shown and activated.
|
||||
* @event
|
||||
*/
|
||||
this.onshow = null;
|
||||
|
||||
/**
|
||||
* Called whenever this component needs to be hidden and deactivated.
|
||||
* @event
|
||||
*/
|
||||
this.onhide = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A Guacamole UI component which can be repositioned by dragging.
|
||||
*
|
||||
* @constructor
|
||||
* @augments GuacUI.Component
|
||||
*/
|
||||
GuacUI.DraggableComponent = function(element) {
|
||||
|
||||
var draggable_component = this;
|
||||
|
||||
var position_x = 0;
|
||||
var position_y = 0;
|
||||
|
||||
var start_x = 0;
|
||||
var start_y = 0;
|
||||
|
||||
/*
|
||||
* Record drag start when finger hits element
|
||||
*/
|
||||
if (element)
|
||||
element.addEventListener("touchstart", function(e) {
|
||||
|
||||
if (e.touches.length == 1) {
|
||||
|
||||
start_x = e.touches[0].screenX;
|
||||
start_y = e.touches[0].screenY;
|
||||
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
}, true);
|
||||
|
||||
/*
|
||||
* Update position based on last touch
|
||||
*/
|
||||
if (element)
|
||||
element.addEventListener("touchmove", function(e) {
|
||||
|
||||
if (e.touches.length == 1) {
|
||||
|
||||
var new_x = e.touches[0].screenX;
|
||||
var new_y = e.touches[0].screenY;
|
||||
|
||||
position_x += new_x - start_x;
|
||||
position_y += new_y - start_y;
|
||||
|
||||
start_x = new_x;
|
||||
start_y = new_y;
|
||||
|
||||
// Move magnifier to new position
|
||||
draggable_component.move(position_x, position_y);
|
||||
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
}, true);
|
||||
|
||||
if (element)
|
||||
element.addEventListener("touchend", function(e) {
|
||||
e.stopPropagation();
|
||||
}, true);
|
||||
|
||||
/**
|
||||
* Moves this component to the specified location relative to its normal
|
||||
* position.
|
||||
*
|
||||
* @param {Number} x The X coordinate in pixels.
|
||||
* @param {Number} y The Y coordinate in pixels.
|
||||
*/
|
||||
this.move = function(x, y) {
|
||||
|
||||
element.style.WebkitTransform =
|
||||
element.style.MozTransform =
|
||||
element.style.OTransform =
|
||||
element.style.msTransform =
|
||||
element.style.transform = "translate("
|
||||
+ x + "px, " + y + "px)";
|
||||
|
||||
if (draggable_component.onmove)
|
||||
draggable_component.onmove(x, y);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigered whenever this element is moved.
|
||||
*
|
||||
* @event
|
||||
* @param {Number} x The new X coordinate.
|
||||
* @param {Number} y The new Y coordinate.
|
||||
*/
|
||||
this.onmove = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A connection UI object which can be easily added to a list of connections
|
||||
* for sake of display.
|
||||
@ -642,7 +493,6 @@ GuacUI.Pager = function(container) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface object which displays the progress of a download, ultimately
|
||||
* becoming a download link once complete.
|
||||
@ -701,6 +551,22 @@ GuacUI.Download = function(filename) {
|
||||
progress.textContent = text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the content of the dialog to reflect an error condition
|
||||
* represented by the given text.
|
||||
*
|
||||
* @param {String} text A human-readable description of the error.
|
||||
*/
|
||||
this.showError = function(text) {
|
||||
|
||||
element.removeChild(progress);
|
||||
GuacUI.addClass(element, "error");
|
||||
|
||||
var status = GuacUI.createChildElement(element, "div", "status");
|
||||
status.textContent = text;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the progress indicator and replaces it with a download button.
|
||||
*/
|
||||
@ -709,7 +575,7 @@ GuacUI.Download = function(filename) {
|
||||
element.removeChild(progress);
|
||||
GuacUI.addClass(element, "complete");
|
||||
|
||||
var download = GuacUI.createChildElement(element, "div", "download");
|
||||
var download = GuacUI.createChildElement(element, "button");
|
||||
download.textContent = "Download";
|
||||
download.onclick = function() {
|
||||
if (guac_download.ondownload)
|
||||
@ -739,6 +605,108 @@ GuacUI.Download = function(filename) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface object which displays the progress of a upload.
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} filename The name the file will have once complete.
|
||||
*/
|
||||
GuacUI.Upload = function(filename) {
|
||||
|
||||
/**
|
||||
* Reference to this GuacUI.Upload.
|
||||
* @private
|
||||
*/
|
||||
var guac_upload = this;
|
||||
|
||||
/**
|
||||
* The outer div representing the notification.
|
||||
* @private
|
||||
*/
|
||||
var element = GuacUI.createElement("div", "upload notification");
|
||||
|
||||
/**
|
||||
* Title bar describing the notification.
|
||||
* @private
|
||||
*/
|
||||
var title = GuacUI.createChildElement(element, "div", "title-bar");
|
||||
|
||||
/**
|
||||
* Close button for removing the notification.
|
||||
* @private
|
||||
*/
|
||||
var close_button = GuacUI.createChildElement(title, "div", "close");
|
||||
close_button.onclick = function() {
|
||||
if (guac_upload.onclose)
|
||||
guac_upload.onclose();
|
||||
};
|
||||
|
||||
GuacUI.createChildElement(title, "div", "title").textContent =
|
||||
"File Transfer";
|
||||
|
||||
GuacUI.createChildElement(element, "div", "caption").textContent =
|
||||
filename + " ";
|
||||
|
||||
/**
|
||||
* Progress bar and status.
|
||||
* @private
|
||||
*/
|
||||
var progress = GuacUI.createChildElement(element, "div", "progress");
|
||||
|
||||
/**
|
||||
* The actual moving bar within the progress bar.
|
||||
* @private
|
||||
*/
|
||||
var bar = GuacUI.createChildElement(progress, "div", "bar");
|
||||
|
||||
/**
|
||||
* The textual readout of progress.
|
||||
* @private
|
||||
*/
|
||||
var progress_status = GuacUI.createChildElement(progress, "div");
|
||||
|
||||
/**
|
||||
* Updates the content of the progress indicator with the given text.
|
||||
*
|
||||
* @param {String} text The text to assign to the progress indicator.
|
||||
* @param {Number} percent The overall percent complete.
|
||||
*/
|
||||
this.updateProgress = function(text, percent) {
|
||||
progress_status.textContent = text;
|
||||
bar.style.width = percent + "%";
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the content of the dialog to reflect an error condition
|
||||
* represented by the given text.
|
||||
*
|
||||
* @param {String} text A human-readable description of the error.
|
||||
*/
|
||||
this.showError = function(text) {
|
||||
|
||||
element.removeChild(progress);
|
||||
GuacUI.addClass(element, "error");
|
||||
|
||||
var status = GuacUI.createChildElement(element, "div", "status");
|
||||
status.textContent = text;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the element representing this notification.
|
||||
*/
|
||||
this.getElement = function() {
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the close button of this notification is clicked.
|
||||
* @event
|
||||
*/
|
||||
this.onclose = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A grouping component. Child elements can be added via the addElement()
|
||||
* function. By default, groups display as collapsed.
|
||||
|
212
guacamole-tunnel/src/main/webapp/scripts/history.js
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set of thumbnails for each connection, indexed by ID.
|
||||
*/
|
||||
GuacamoleHistory = new (function() {
|
||||
|
||||
/**
|
||||
* Reference to this GuacamoleHistory.
|
||||
*/
|
||||
var guac_history = this;
|
||||
|
||||
/**
|
||||
* The number of entries to allow before removing old entries based on the
|
||||
* cutoff.
|
||||
*/
|
||||
var IDEAL_LENGTH = 6;
|
||||
|
||||
/**
|
||||
* The maximum age of a history entry before it is removed, in
|
||||
* milliseconds.
|
||||
*/
|
||||
var CUTOFF_AGE = 900000;
|
||||
|
||||
var history = {};
|
||||
|
||||
function truncate() {
|
||||
|
||||
// Build list of entries
|
||||
var entries = [];
|
||||
for (var old_id in history)
|
||||
entries.push(history[old_id]);
|
||||
|
||||
// Avoid history growth beyond defined number of entries
|
||||
if (entries.length > IDEAL_LENGTH) {
|
||||
|
||||
// Sort list
|
||||
entries.sort(GuacamoleHistory.Entry.compare);
|
||||
|
||||
// Remove entries until length is ideal or all are recent
|
||||
var now = new Date().getTime();
|
||||
while (entries.length > IDEAL_LENGTH
|
||||
&& now - entries[0].accessed > CUTOFF_AGE) {
|
||||
|
||||
// Remove entry
|
||||
var removed = entries.shift();
|
||||
delete history[removed.id];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL for the thumbnail of the connection with the given ID,
|
||||
* or undefined if no thumbnail is associated with that connection.
|
||||
*/
|
||||
this.get = function(id) {
|
||||
return history[id] || new GuacamoleHistory.Entry();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the thumbnail and access time of the history entry for the
|
||||
* connection with the given ID.
|
||||
*/
|
||||
this.update = function(id, thumbnail) {
|
||||
|
||||
/* Do nothing if localStorage not present */
|
||||
if (!localStorage)
|
||||
return;
|
||||
|
||||
// Create updated entry
|
||||
var entry = new GuacamoleHistory.Entry(
|
||||
id,
|
||||
thumbnail,
|
||||
new Date().getTime()
|
||||
);
|
||||
|
||||
// Store entry in history
|
||||
history[id] = entry;
|
||||
truncate();
|
||||
|
||||
// Save updated history, ignore inability to use localStorage
|
||||
try {
|
||||
localStorage.setItem("GUAC_HISTORY", JSON.stringify(history));
|
||||
}
|
||||
catch (ignore) {}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Reloads all history data.
|
||||
*/
|
||||
this.reload = function() {
|
||||
|
||||
/* Do nothing if localStorage not present */
|
||||
if (!localStorage)
|
||||
return;
|
||||
|
||||
// Get old and new for comparison, ignore inability to use localStorage
|
||||
var old_history = history;
|
||||
try {
|
||||
var new_history = JSON.parse(localStorage.getItem("GUAC_HISTORY") || "{}");
|
||||
}
|
||||
catch (ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update history
|
||||
history = new_history;
|
||||
|
||||
// Call onchange handler as necessary
|
||||
if (guac_history.onchange) {
|
||||
|
||||
// Produce union of all known IDs
|
||||
var known_ids = {};
|
||||
for (var new_id in new_history) known_ids[new_id] = true;
|
||||
for (var old_id in old_history) known_ids[old_id] = true;
|
||||
|
||||
// For each known ID
|
||||
for (var id in known_ids) {
|
||||
|
||||
// Get entries
|
||||
var old_entry = old_history[id];
|
||||
var new_entry = new_history[id];
|
||||
|
||||
// Call handler for all changed
|
||||
if (!old_entry || !new_entry
|
||||
|| old_entry.accessed != new_entry.accessed)
|
||||
guac_history.onchange(id, old_entry, new_entry);
|
||||
|
||||
}
|
||||
|
||||
} // end onchange
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler called whenever a history entry is changed.
|
||||
*
|
||||
* @event
|
||||
* @param {String} id The ID of the connection whose history entry is
|
||||
* changing.
|
||||
* @param {GuacamoleHistory.Entry} old_entry The old value of the entry, if
|
||||
* any.
|
||||
* @param {GuacamoleHistory.Entry} new_entry The new value of the entry, if
|
||||
* any.
|
||||
*/
|
||||
this.onchange = null;
|
||||
|
||||
// Reload when modified
|
||||
window.addEventListener("storage", guac_history.reload, false);
|
||||
|
||||
// Initial load
|
||||
guac_history.reload();
|
||||
|
||||
})();
|
||||
|
||||
/**
|
||||
* A single entry in the indexed connection usage history.
|
||||
*
|
||||
* @constructor
|
||||
* @param {String} id The ID of this connection.
|
||||
* @param {String} thumbnail The URL of the thumbnail to use to represent this
|
||||
* connection.
|
||||
* @param {Number} last_access The time this connection was last accessed, in
|
||||
* seconds.
|
||||
*/
|
||||
GuacamoleHistory.Entry = function(id, thumbnail, last_access) {
|
||||
|
||||
/**
|
||||
* The ID of the connection associated with this history entry.
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* The thumbnail associated with the connection associated with this history
|
||||
* entry.
|
||||
*/
|
||||
this.thumbnail = thumbnail;
|
||||
|
||||
/**
|
||||
* The time the connection associated with this entry was last accessed.
|
||||
*/
|
||||
this.accessed = last_access;
|
||||
|
||||
};
|
||||
|
||||
GuacamoleHistory.Entry.compare = function(a, b) {
|
||||
return a.accessed - b.accessed;
|
||||
};
|
@ -1,19 +1,23 @@
|
||||
/*
|
||||
* Guacamole - Clientless Remote Desktop
|
||||
* Copyright (C) 2010 Michael Jumper
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -321,19 +325,22 @@ GuacamoleService.PermissionSet = function() {
|
||||
GuacamoleService.handleResponse = function(xhr) {
|
||||
|
||||
// For HTTP Forbidden, just return permission denied
|
||||
if (xhr.status == 403)
|
||||
throw new Error("Permission denied.");
|
||||
if (xhr.status === 403)
|
||||
throw new Guacamole.Status(Guacamole.Status.Code.CLIENT_FORBIDDEN, "Permission denied.");
|
||||
|
||||
// Otherwise, if unsuccessful, throw error with message derived from
|
||||
// response
|
||||
if (xhr.status != 200) {
|
||||
if (xhr.status !== 200) {
|
||||
|
||||
// Retrieve error code
|
||||
var code = parseInt(xhr.getResponseHeader("Guacamole-Status-Code"));
|
||||
|
||||
// Retrieve error message
|
||||
var message = xhr.getResponseHeader("Guacamole-Error-Message")
|
||||
|| xhr.statusText;
|
||||
|
||||
// Throw error with derived message
|
||||
throw new Error(message);
|
||||
throw new Guacamole.Status(code, message);
|
||||
|
||||
}
|
||||
|
||||
@ -1272,6 +1279,11 @@ GuacamoleService.Protocol.Parameter.BOOLEAN = 3;
|
||||
*/
|
||||
GuacamoleService.Protocol.Parameter.ENUM = 4;
|
||||
|
||||
/**
|
||||
* A free-form, multi-line text field.
|
||||
*/
|
||||
GuacamoleService.Protocol.Parameter.MULTILINE = 5;
|
||||
|
||||
/**
|
||||
* Collection of service functions which deal with protocols. Each function
|
||||
* makes an explicit HTTP query to the server, and parses the response.
|
||||
@ -1364,6 +1376,11 @@ GuacamoleService.Protocols = {
|
||||
parameter.type = GuacamoleService.Protocol.Parameter.ENUM;
|
||||
break;
|
||||
|
||||
// Multiline text parameter
|
||||
case "multiline":
|
||||
parameter.type = GuacamoleService.Protocol.Parameter.MULTILINE;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Parse all options
|
||||
@ -1396,3 +1413,28 @@ GuacamoleService.Protocols = {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Collection of service functions which deal with the clipboard state. Each
|
||||
* function makes an explicit HTTP query to the server, and parses the response.
|
||||
*/
|
||||
GuacamoleService.Clipboard = {
|
||||
|
||||
"get" : function(parameters) {
|
||||
|
||||
// Construct request URL
|
||||
var list_url = "clipboard";
|
||||
if (parameters) list_url += "?" + parameters;
|
||||
|
||||
// Get permission list
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", list_url, false);
|
||||
xhr.send(null);
|
||||
|
||||
// Handle response
|
||||
GuacamoleService.handleResponse(xhr);
|
||||
return xhr.responseText;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,107 +1,160 @@
|
||||
|
||||
/*
|
||||
* Guacamole - Clientless Remote Desktop
|
||||
* Copyright (C) 2010 Michael Jumper
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Maintains state across multiple Guacamole pages via HTML5 Web Storage.
|
||||
* @constructor
|
||||
* Global storage for Guacamole pages.
|
||||
*/
|
||||
function GuacamoleSessionState() {
|
||||
GuacamoleSessionStorage = (opener && opener.GuacamoleSessionStorage) || new (function() {
|
||||
|
||||
/**
|
||||
* Reference to this GuacamoleSessionState.
|
||||
* The contents of storage, as a JSON string containing name/value pairs as
|
||||
* properties.
|
||||
*
|
||||
* @private
|
||||
* @type String
|
||||
*/
|
||||
var guac_state = this;
|
||||
var stored_json = "{}";
|
||||
|
||||
/**
|
||||
* The last read state object.
|
||||
* @private
|
||||
* Called whenever an item value changes.
|
||||
*
|
||||
* @callback onchange
|
||||
* @param {String} name The name of the item changed.
|
||||
* @param value The new item value.
|
||||
*/
|
||||
var state = localStorage.getItem("GUACAMOLE_STATE") || {};
|
||||
|
||||
/**
|
||||
* Reloads the internal state, sending onchange events for all changed,
|
||||
* deleted, or new properties.
|
||||
* All attached listeners.
|
||||
*
|
||||
* @type onchange[]
|
||||
*/
|
||||
this.reload = function() {
|
||||
var listeners = [];
|
||||
|
||||
/**
|
||||
* Notifies all listeners that an item has changed.
|
||||
*
|
||||
* @param {String} name The name of the item that changed.
|
||||
* @param value The new item value.
|
||||
*/
|
||||
function __notify_changed(name, value) {
|
||||
for (var i=0; i<listeners.length; i++)
|
||||
listeners[i](name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value stored within the item having the given name.
|
||||
*
|
||||
* @param {String} name The name of the item to read.
|
||||
* @param [value] The default value, if any.
|
||||
* @return The value of the given item.
|
||||
*/
|
||||
this.getItem = function(name, value) {
|
||||
|
||||
// Attempt to read JSON from localStorage, default to local variable
|
||||
var json = stored_json;
|
||||
if (localStorage) {
|
||||
try {
|
||||
json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
||||
}
|
||||
catch (ignore) {}
|
||||
}
|
||||
|
||||
var obj = JSON.parse(json);
|
||||
if (obj[name] !== undefined)
|
||||
return obj[name];
|
||||
|
||||
return value;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the item having the given name to the given value.
|
||||
*
|
||||
* @param {String} name The name of the item to change.
|
||||
* @param [value] An arbitrary value.
|
||||
*/
|
||||
this.setItem = function(name, value) {
|
||||
|
||||
// Attempt to read JSON from localStorage, default to local variable
|
||||
var json = stored_json;
|
||||
if (localStorage) {
|
||||
try {
|
||||
json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
||||
}
|
||||
catch (ignore) {}
|
||||
}
|
||||
|
||||
// Modify object property
|
||||
var obj = JSON.parse(json);
|
||||
var old = obj[name];
|
||||
obj[name] = value;
|
||||
|
||||
// Notify of change
|
||||
if (old !== value)
|
||||
__notify_changed(name, value);
|
||||
|
||||
// Attempt to set JSON within localStorage, default to local variable
|
||||
stored_json = JSON.stringify(obj);
|
||||
if (localStorage) {
|
||||
try {
|
||||
localStorage.setItem("GUACAMOLE_STATE", stored_json);
|
||||
}
|
||||
catch (ignore) {}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Reload when modified
|
||||
window.addEventListener("storage", function reload() {
|
||||
|
||||
// Pull current state
|
||||
var new_state = JSON.parse(localStorage.getItem("GUACAMOLE_STATE") || "{}");
|
||||
var new_json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
||||
|
||||
// Assign new state
|
||||
var old_state = state;
|
||||
state = new_state;
|
||||
var new_state = JSON.parse(new_json);
|
||||
var old_state = JSON.parse(stored_json);
|
||||
|
||||
// Check if any values are different
|
||||
for (var name in new_state) {
|
||||
|
||||
// If value changed, call handler
|
||||
// If value changed, notify
|
||||
var old = old_state[name];
|
||||
if (old != new_state[name]) {
|
||||
|
||||
// Call change handler
|
||||
if (guac_state.onchange)
|
||||
guac_state.onchange(state, new_state, name);
|
||||
if (old !== new_state[name])
|
||||
__notify_changed(name, new_state[name]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
stored_json = new_json;
|
||||
|
||||
};
|
||||
}, false);
|
||||
|
||||
/**
|
||||
* Sets the given property to the given value.
|
||||
* Ensures that the given function will be called for each change in
|
||||
* item value. The function must accept a single argument which will be
|
||||
* the name of the item changed.
|
||||
*
|
||||
* @param {String} name The name of the property to change.
|
||||
* @param value An arbitrary value.
|
||||
* @param {onchange} onchange The function to call when an item changes.
|
||||
*/
|
||||
this.setProperty = function(name, value) {
|
||||
state[name] = value;
|
||||
localStorage.setItem("GUACAMOLE_STATE", JSON.stringify(state));
|
||||
this.addChangeListener = function(onchange) {
|
||||
listeners.push(onchange);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the value stored under the property having the given name.
|
||||
*
|
||||
* @param {String} name The name of the property to read.
|
||||
* @return The value of the given property.
|
||||
*/
|
||||
this.getProperty = function(name) {
|
||||
return state[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Event which is fired whenever a property value is changed externally.
|
||||
*
|
||||
* @event
|
||||
* @param old_state An object whose properties' values are the old values
|
||||
* of this GuacamoleSessionState.
|
||||
* @param new_state An object whose properties' values are the new values
|
||||
* of this GuacamoleSessionState.
|
||||
* @param {String} name The name of the property that is being changed.
|
||||
*/
|
||||
this.onchange = null;
|
||||
|
||||
// Reload when modified
|
||||
window.addEventListener("storage", guac_state.reload, false);
|
||||
|
||||
// Initial load
|
||||
guac_state.reload();
|
||||
|
||||
}
|
||||
})();
|
||||
|
65
guacamole-tunnel/src/main/webapp/styles/admin.css
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
button#back {
|
||||
|
||||
background-image: url('../images/action-icons/guac-back.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
||||
|
||||
button#add-user {
|
||||
|
||||
background-image: url('../images/action-icons/guac-user-add.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
||||
|
||||
button#add-connection {
|
||||
|
||||
background-image: url('../images/action-icons/guac-monitor-add.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
||||
|
||||
button#add-connection-group {
|
||||
|
||||
background-image: url('../images/action-icons/guac-group-add.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
53
guacamole-tunnel/src/main/webapp/styles/animation.css
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* fadein: Fade from fully transparent to fully opaque.
|
||||
*/
|
||||
@keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-moz-keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-webkit-keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/**
|
||||
* fadeout: Fade from fully opaque to fully transparent.
|
||||
*/
|
||||
@keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
@-moz-keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
@-webkit-keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
@ -1,20 +1,23 @@
|
||||
|
||||
/*
|
||||
* Guacamole - Clientless Remote Desktop
|
||||
* Copyright (C) 2010 Michael Jumper
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* 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/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
body {
|
||||
@ -22,6 +25,7 @@ body {
|
||||
font-family: FreeSans, Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
@ -29,8 +33,9 @@ img {
|
||||
}
|
||||
|
||||
.software-cursor {
|
||||
cursor: url('../images/mouse/dot.gif'),url('../images/mouse/blank.cur'),default;
|
||||
cursor: url('../images/mouse/blank.gif'),url('../images/mouse/blank.cur'),default;
|
||||
overflow: hidden;
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.guac-error .software-cursor {
|
||||
@ -65,27 +70,6 @@ div.dialogMiddle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.dialog {
|
||||
padding: 1em;
|
||||
|
||||
max-width: 75%;
|
||||
text-align: left;
|
||||
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.dialog h1 {
|
||||
margin: 0;
|
||||
margin-bottom: 0.25em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.dialog div.buttons {
|
||||
margin: 0;
|
||||
margin-top: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
border-style: solid;
|
||||
@ -130,6 +114,11 @@ div.dialog p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#main {
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
div.displayOuter {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@ -256,15 +245,63 @@ div#viewportClone {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.status {
|
||||
text-shadow: 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black;
|
||||
font-size: xx-large;
|
||||
color: white;
|
||||
@keyframes show-dialog {
|
||||
0% {transform: scale(0.75); }
|
||||
100% {transform: scale(1); }
|
||||
}
|
||||
|
||||
.guac-error .status {
|
||||
text-shadow: 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black;
|
||||
color: #D44;
|
||||
@-webkit-keyframes show-dialog {
|
||||
0% {-webkit-transform: scale(0.75); }
|
||||
100% {-webkit-transform: scale(1); }
|
||||
}
|
||||
|
||||
.dialog {
|
||||
|
||||
animation-name: show-dialog;
|
||||
animation-timing-function: linear;
|
||||
animation-duration: 0.125s;
|
||||
-webkit-animation-name: show-dialog;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-duration: 0.125s;
|
||||
|
||||
max-width: 75%;
|
||||
max-height: none;
|
||||
width: 4in;
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
|
||||
padding: 0.5em;
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
.guac-error .dialog {
|
||||
background: #FDD;
|
||||
border: 1px solid #964040;
|
||||
}
|
||||
|
||||
.dialog .title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid black;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.dialog .status, .dialog .countdown, .dialog .reconnect {
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.dialog .reconnect {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.guac-error .dialog .reconnect {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p.hint {
|
||||
@ -297,24 +334,29 @@ p.hint {
|
||||
|
||||
.notification {
|
||||
|
||||
font-size: 0.9em;
|
||||
font-size: 0.7em;
|
||||
text-align: center;
|
||||
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
background: black;
|
||||
opacity: 0.9;
|
||||
border: 1px solid rgba(0, 0, 0, 0.75);
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
background: white;
|
||||
|
||||
color: white;
|
||||
color: black;
|
||||
|
||||
padding: 0.5em;
|
||||
margin: 1em;
|
||||
overflow: hidden;
|
||||
|
||||
box-shadow: 0.25em 0.25em 0.25em rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.25);
|
||||
|
||||
}
|
||||
|
||||
.notification div {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.notification .title-bar {
|
||||
@ -322,7 +364,7 @@ p.hint {
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
|
||||
border-bottom: 1px solid white;
|
||||
border-bottom: 1px solid black;
|
||||
padding-bottom: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
@ -331,10 +373,6 @@ p.hint {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.notification .caption {
|
||||
color: silver;
|
||||
}
|
||||
|
||||
.notification .close {
|
||||
|
||||
background: url('../images/action-icons/guac-close.png');
|
||||
@ -361,6 +399,7 @@ p.hint {
|
||||
to {background-position: 64px 0px;}
|
||||
}
|
||||
|
||||
.notification .caption,
|
||||
.download.notification .caption {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
@ -368,7 +407,15 @@ p.hint {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.upload.notification .status,
|
||||
.download.notification .status {
|
||||
color: red;
|
||||
font-size: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.download.notification .progress,
|
||||
.upload.notification .progress,
|
||||
.download.notification .download {
|
||||
|
||||
margin-top: 1em;
|
||||
@ -377,16 +424,47 @@ p.hint {
|
||||
min-width: 5em;
|
||||
|
||||
border: 1px solid gray;
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
|
||||
text-align: center;
|
||||
float: right;
|
||||
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.upload.notification .progress {
|
||||
float: none;
|
||||
width: 80%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.download.notification .progress div,
|
||||
.upload.notification .progress div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.download.notification .progress .bar,
|
||||
.upload.notification .progress .bar {
|
||||
background: #A3D655;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.5),
|
||||
inset -1px -1px 0 rgba( 0, 0, 0, 0.1),
|
||||
1px 1px 0 gray;
|
||||
}
|
||||
|
||||
.upload.notification .progress,
|
||||
.download.notification .progress {
|
||||
|
||||
background: #444 url('../images/progress.png');
|
||||
background: #C2C2C2 url('../images/progress.png');
|
||||
background-size: 16px 16px;
|
||||
-moz-background-size: 16px 16px;
|
||||
-webkit-background-size: 16px 16px;
|
||||
@ -418,3 +496,284 @@ p.hint {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
|
||||
/**
|
||||
* show-menu: Animation for showing the menu
|
||||
*/
|
||||
@keyframes show-menu {
|
||||
from { left: -480px; opacity: 0;}
|
||||
to { left: 0; opacity: 1;}
|
||||
}
|
||||
@-moz-keyframes show-menu {
|
||||
from { left: -480px; opacity: 0;}
|
||||
to { left: 0; opacity: 1;}
|
||||
}
|
||||
@-webkit-keyframes show-menu {
|
||||
from { left: -480px; opacity: 0;}
|
||||
to { left: 0; opacity: 1;}
|
||||
}
|
||||
|
||||
/**
|
||||
* hide-menu: Animation for hiding the menu
|
||||
*/
|
||||
@keyframes hide-menu {
|
||||
from { left: 0; opacity: 1;}
|
||||
to { left: -480px; opacity: 0;}
|
||||
}
|
||||
@-moz-keyframes hide-menu {
|
||||
from { left: 0; opacity: 1;}
|
||||
to { left: -480px; opacity: 0;}
|
||||
}
|
||||
@-webkit-keyframes hide-menu {
|
||||
from { left: 0; opacity: 1;}
|
||||
to { left: -480px; opacity: 0;}
|
||||
}
|
||||
|
||||
#menu {
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
max-width: 100%;
|
||||
width: 480px;
|
||||
background: #EEE;
|
||||
box-shadow: inset -1px 0 2px white, 1px 0 2px black;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#menu .content {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#menu .content > * {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#menu, #menu.closed {
|
||||
left: -480px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#menu.open {
|
||||
left: 0px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#menu.closed {
|
||||
animation-name: hide-menu;
|
||||
animation-timing-function: linear;
|
||||
animation-duration: 0.05s;
|
||||
-webkit-animation-name: hide-menu;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-duration: 0.05s;
|
||||
}
|
||||
|
||||
#menu.open {
|
||||
animation-name: show-menu;
|
||||
animation-timing-function: linear;
|
||||
animation-duration: 0.05s;
|
||||
-webkit-animation-name: show-menu;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-duration: 0.05s;
|
||||
}
|
||||
|
||||
#clipboard-settings textarea {
|
||||
width: 100%;
|
||||
border: 1px solid #AAA;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
white-space: pre;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#mouse-settings .choice {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#mouse-settings .choice .figure {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
#keyboard-settings .caption {
|
||||
font-size: 0.9em;
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
#mouse-settings .figure .caption {
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
#mouse-settings .figure img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
#menu h3 {
|
||||
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
|
||||
padding: 0.25em 0.5em;
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
#keyboard-settings .figure {
|
||||
float: right;
|
||||
max-width: 30%;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#keyboard-settings .figure img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#zoom-settings {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#zoom-out, #zoom-in, #zoom-state {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#zoom-out, #zoom-in {
|
||||
max-width: 3em;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2em;
|
||||
margin: 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#zoom-out img, #zoom-in img {
|
||||
max-width: 100%;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#zoom-out:hover, #zoom-in:hover {
|
||||
border: 1px solid rgba(0, 0, 0, 1);
|
||||
background: #CDA;
|
||||
}
|
||||
|
||||
#zoom-out:hover img, #zoom-in:hover img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#zoom-state {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#text-input {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: #CDA;
|
||||
|
||||
}
|
||||
|
||||
#text-input-field,
|
||||
#text-input-buttons {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#text-input-field {
|
||||
width: 30%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#text-input-buttons {
|
||||
width: 70%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#target {
|
||||
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 12pt;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
resize: none;
|
||||
outline: none;
|
||||
|
||||
margin: 0;
|
||||
padding: 0.25em;
|
||||
padding-left: 0;
|
||||
background: #CDA;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
#text-input.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sent-history {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.25em;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#sent-history .sent-text {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
white-space: pre;
|
||||
font-size: 12pt;
|
||||
|
||||
animation: fadeout 1s linear;
|
||||
-webkit-animation: fadeout 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#text-input-buttons button {
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: none;
|
||||
color: black;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
padding: 0.25em;
|
||||
max-width: 20%;
|
||||
margin: 0.1em;
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
#text-input-buttons button:active,
|
||||
#text-input-buttons button.pressed {
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.notification.message {
|
||||
background: #DFD;
|
||||
animation: fadein 0.125s linear, fadeout 2s 3s linear;
|
||||
-webkit-animation: fadein 0.125s linear, fadeout 2s 3s linear;
|
||||
}
|
||||
|
||||
.notification.message .caption {
|
||||
vertical-align: middle;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
153
guacamole-tunnel/src/main/webapp/styles/keyboard.css
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.keyboard-container {
|
||||
text-align: center;
|
||||
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
border-top: 1px solid black;
|
||||
background: #222;
|
||||
opacity: 0.85;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.guac-keyboard {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: default;
|
||||
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key {
|
||||
background: #444;
|
||||
border: 1px outset #888;
|
||||
-moz-border-radius: 0.1em;
|
||||
-webkit-border-radius: 0.1em;
|
||||
-khtml-border-radius: 0.1em;
|
||||
border-radius: 0.1em;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-cap {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 50%;
|
||||
font-weight: lighter;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.highlight {
|
||||
background: #666;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key.shift,
|
||||
.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key.control,
|
||||
.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key.alt,
|
||||
.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-super .guac-keyboard-key.super,
|
||||
.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.guac-keyboard-pressed {
|
||||
background: #822;
|
||||
border-color: #D44;
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-row {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-column {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-gap {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Hide keycaps requiring modifiers which are NOT currently active. */
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-caps)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-caps,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-numsym)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-numsym,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-shift)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-shift,
|
||||
|
||||
/* Hide keycaps NOT requiring modifiers which ARE currently active, where that
|
||||
modifier is used to determine which cap is displayed for the current key. */
|
||||
.guac-keyboard.guac-keyboard-modifier-shift
|
||||
.guac-keyboard-key.guac-keyboard-uses-shift
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-shift),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-numsym
|
||||
.guac-keyboard-key.guac-keyboard-uses-numsym
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-numsym),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-caps
|
||||
.guac-keyboard-key.guac-keyboard-uses-caps
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-caps) {
|
||||
|
||||
display: none;
|
||||
|
||||
}
|
364
guacamole-tunnel/src/main/webapp/styles/login.css
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
input[type=checkbox], input[type=text], textarea {
|
||||
-webkit-tap-highlight-color: rgba(128,192,128,0.5);
|
||||
}
|
||||
|
||||
input[type=submit], button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #EEE;
|
||||
font-family: FreeSans, Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#manage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admin #manage {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button#manage {
|
||||
|
||||
background-image: url('../images/action-icons/guac-config.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
||||
|
||||
div#login-ui {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: table;
|
||||
}
|
||||
|
||||
@keyframes shake-head {
|
||||
0% { margin-left: 0.25em; margin-right: -0.25em; }
|
||||
25% { margin-left: -0.25em; margin-right: 0.25em; }
|
||||
50% { margin-left: 0.25em; margin-right: -0.25em; }
|
||||
75% { margin-left: -0.25em; margin-right: 0.25em; }
|
||||
100% { margin-left: 0.00em; margin-right: 0.00em; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes shake-head {
|
||||
0% { margin-left: 0.25em; margin-right: -0.25em; }
|
||||
25% { margin-left: -0.25em; margin-right: 0.25em; }
|
||||
50% { margin-left: 0.25em; margin-right: -0.25em; }
|
||||
75% { margin-left: -0.25em; margin-right: 0.25em; }
|
||||
100% { margin-left: 0.00em; margin-right: 0.00em; }
|
||||
}
|
||||
|
||||
p#login-error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error p#login-error {
|
||||
display: block;
|
||||
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
padding: 1em;
|
||||
margin: 0.2em;
|
||||
|
||||
background: #FDD;
|
||||
border: 1px solid #964040;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
text-align: center;
|
||||
color: #964040;
|
||||
}
|
||||
|
||||
.error #login-form {
|
||||
animation-name: shake-head;
|
||||
animation-duration: 0.25s;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-name: shake-head;
|
||||
-webkit-animation-duration: 0.25s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
}
|
||||
|
||||
div#login-logo {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div#login-dialog-middle {
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#login-dialog {
|
||||
|
||||
max-width: 75%;
|
||||
text-align: left;
|
||||
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div#login-dialog h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#login-dialog #buttons {
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input[type="submit"]#login, button#login {
|
||||
|
||||
background-image: url('../images/guacamole-logo-64.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5em;
|
||||
background-position: 0.5em 0.25em;
|
||||
|
||||
padding-left: 2.5em;
|
||||
|
||||
}
|
||||
|
||||
div#login-dialog #login-fields {
|
||||
|
||||
vertical-align: middle;
|
||||
|
||||
padding: 1em;
|
||||
border-top: 1px solid #999;
|
||||
border-bottom: 1px solid #999;
|
||||
|
||||
}
|
||||
|
||||
div#login-dialog th {
|
||||
text-shadow: 1px 1px white;
|
||||
}
|
||||
|
||||
div#login-dialog #login-fields input {
|
||||
border: 1px solid #777;
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div#login-dialog #login-fields img.logo {
|
||||
position: fixed;
|
||||
margin: 10px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.1;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
div#version {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
font-size: 0.75em;
|
||||
color: black;
|
||||
opacity: 0.5;
|
||||
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
img#license {
|
||||
float: right;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
div#connection-list-ui h1 {
|
||||
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
font-size: 2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
div#connection-list-ui h2 {
|
||||
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
font-size: 1.5em;
|
||||
|
||||
font-weight: lighter;
|
||||
text-shadow: 1px 1px white;
|
||||
|
||||
border-top: 1px solid #AAA;
|
||||
border-bottom: 1px solid #AAA;
|
||||
background: #DDD;
|
||||
|
||||
}
|
||||
|
||||
div#connection-list-ui img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div#logout-panel {
|
||||
padding: 0.45em;
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.history-unavailable div#recent-connections {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#recent-connections,
|
||||
div#clipboardDiv,
|
||||
div#settings,
|
||||
div#all-connections {
|
||||
margin: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#all-connections .list-buttons {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div#recent-connections {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#no-recent {
|
||||
|
||||
color: black;
|
||||
text-shadow: 1px 1px white;
|
||||
opacity: 0.5;
|
||||
|
||||
font-size: 2em;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
div#recent-connections div.connection {
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
-khtml-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
display: inline-block;
|
||||
padding: 1em;
|
||||
margin: 1em;
|
||||
text-align: center;
|
||||
max-width: 75%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.group,
|
||||
.connection {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.connection:hover {
|
||||
background: #CDA;
|
||||
}
|
||||
|
||||
.group,
|
||||
.connection .name {
|
||||
color: black;
|
||||
font-weight: normal;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.connection .thumbnail {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.connection .thumbnail img {
|
||||
border: 1px solid black;
|
||||
box-shadow: 1px 1px 5px black;
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
div#all-connections .connection {
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div#recent-connections .connection .thumbnail {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div#all-connections .connection {
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
div#recent-connections .protocol {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.caption * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.caption .name {
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
|
||||
#clipboardDiv textarea {
|
||||
width: 100%;
|
||||
border: 1px solid #AAA;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#settings dt {
|
||||
border-bottom: 1px dotted #AAA;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
#settings dd {
|
||||
margin: 1.5em;
|
||||
margin-left: 2.5em;
|
||||
font-size: 0.75em;
|
||||
}
|
626
guacamole-tunnel/src/main/webapp/styles/ui.css
Normal file
@ -0,0 +1,626 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
@import url('animation.css');
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type=checkbox], input[type=number], input[type=text], input[type=radio], label, textarea {
|
||||
-webkit-tap-highlight-color: rgba(128,192,128,0.5);
|
||||
}
|
||||
|
||||
input[type=submit], button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
div.location, input[type=text], input[type=number], input[type=password], textarea {
|
||||
border: 1px solid #777;
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
width: 100%;
|
||||
max-width: 16em;
|
||||
padding: 0.25em;
|
||||
font-size: 10pt;
|
||||
background: white;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
textarea {
|
||||
max-width: none;
|
||||
width: 30em;
|
||||
height: 10em;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
input[type="submit"], button {
|
||||
|
||||
background-color: #3C3C3C;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
color: white;
|
||||
text-shadow: -1px -1px rgba(0, 0, 0, 0.3);
|
||||
font-size: 1em;
|
||||
|
||||
box-shadow: inset -1px -1px 0.1em rgba(0, 0, 0, 0.25),
|
||||
inset 1px 1px 0.1em rgba(255, 255, 255, 0.25),
|
||||
-1px -1px 0.1em rgba(0, 0, 0, 0.25),
|
||||
1px 1px 0.1em rgba(255, 255, 255, 0.25);
|
||||
|
||||
padding: 0.35em;
|
||||
padding-right: 1em;
|
||||
padding-left: 1em;
|
||||
min-width: 5em;
|
||||
|
||||
}
|
||||
|
||||
input[type="submit"]:hover, button:hover {
|
||||
background-color: #5A5A5A;
|
||||
}
|
||||
|
||||
input[type="submit"]:active, button:active {
|
||||
|
||||
background-color: #2C2C2C;
|
||||
|
||||
box-shadow:
|
||||
inset 1px 1px 0.25em rgba(0, 0, 0, 0.25),
|
||||
-1px -1px 0.25em rgba(0, 0, 0, 0.25),
|
||||
1px 1px 0.25em rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
button.danger {
|
||||
background: #A43;
|
||||
}
|
||||
|
||||
button.danger:hover {
|
||||
background: #C54;
|
||||
}
|
||||
|
||||
button.danger:active {
|
||||
background: #932;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #EEE;
|
||||
font-family: FreeSans, Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
font-size: 2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
||||
border-top: 1px solid #AAA;
|
||||
border-bottom: 1px solid #AAA;
|
||||
background: rgba(0, 0, 0, 0.07);
|
||||
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
font-size: 1.5em;
|
||||
|
||||
font-weight: lighter;
|
||||
text-shadow: 1px 1px white;
|
||||
|
||||
}
|
||||
|
||||
div.section {
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialogs
|
||||
*/
|
||||
|
||||
.dialog-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
|
||||
max-width: 100%;
|
||||
width: 8in;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: #E7E7E7;
|
||||
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
|
||||
box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.6);
|
||||
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.dialog .header {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dialog td {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dialog .overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dialog .dropdown {
|
||||
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
margin-top: -1px;
|
||||
|
||||
width: 3in;
|
||||
max-height: 2in;
|
||||
overflow: auto;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: white;
|
||||
|
||||
font-size: 10pt;
|
||||
|
||||
}
|
||||
|
||||
.dialog .footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
* List elements
|
||||
*/
|
||||
|
||||
.list-item {
|
||||
|
||||
display: block;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-size: 16px 16px;
|
||||
-moz-background-size: 16px 16px;
|
||||
-webkit-background-size: 16px 16px;
|
||||
-khtml-background-size: 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
opacity: 0.5;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.list-item * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.list-item .caption {
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.list-item .name {
|
||||
color: black;
|
||||
font-weight: normal;
|
||||
padding: 0.1em;
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
|
||||
.list-item .usage {
|
||||
float: right;
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.list-item.in-use {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.choice .list-item .usage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.choice .list-item.in-use {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* List element styling
|
||||
*/
|
||||
|
||||
.list-item.selected {
|
||||
background: #DEB;
|
||||
}
|
||||
|
||||
.list-item.selected > .icon {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.list-item:not(.selected) .caption:hover {
|
||||
background: #CDA;
|
||||
}
|
||||
|
||||
.choice .list-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.choice input[type='checkbox'] {
|
||||
vertical-align: top;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.disabled .list-item:not(.selected) {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.disabled .list-item:not(.selected):hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
* List element fields (editing)
|
||||
*/
|
||||
|
||||
/*
|
||||
.form {
|
||||
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
z-index: 1;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
background: #E7E7E7;
|
||||
padding: 0;
|
||||
margin: 0.25em;
|
||||
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
|
||||
box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.6);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
.form .fields th,
|
||||
.form .permissions th {
|
||||
font-weight: normal;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.form h2 {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.form h3 {
|
||||
font-size: 1em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.form {
|
||||
cursor: auto;
|
||||
animation-name: fadein;
|
||||
-webkit-animation-name: fadein;
|
||||
animation-duration: 0.125s;
|
||||
-webkit-animation-duration: 0.125s;
|
||||
}
|
||||
|
||||
.object-buttons {
|
||||
|
||||
text-align: right;
|
||||
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding-top: 0.5em;
|
||||
margin: 0.5em;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* List element icons
|
||||
*/
|
||||
|
||||
.icon.user {
|
||||
background-image: url('../images/user-icons/guac-user.png');
|
||||
}
|
||||
|
||||
.icon.user.add {
|
||||
background-image: url('../images/action-icons/guac-user-add.png');
|
||||
}
|
||||
|
||||
.icon.connection {
|
||||
background-image: url('../images/protocol-icons/guac-plug.png');
|
||||
}
|
||||
|
||||
.icon.connection.add {
|
||||
background-image: url('../images/action-icons/guac-monitor-add.png');
|
||||
}
|
||||
|
||||
.protocol {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.protocol .icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-image: url('../images/protocol-icons/guac-plug.png');
|
||||
background-size: 16px 16px;
|
||||
-moz-background-size: 16px 16px;
|
||||
-webkit-background-size: 16px 16px;
|
||||
-khtml-background-size: 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.protocol .icon.ssh,
|
||||
.protocol .icon.telnet {
|
||||
background-image: url('../images/protocol-icons/guac-text.png');
|
||||
}
|
||||
|
||||
.protocol .icon.vnc,
|
||||
.protocol .icon.rdp {
|
||||
background-image: url('../images/protocol-icons/guac-monitor.png');
|
||||
}
|
||||
|
||||
.connection .thumbnail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Groups
|
||||
*/
|
||||
|
||||
.group > .children {
|
||||
margin-left: 13px;
|
||||
padding-left: 6px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.group.expanded > .children {
|
||||
display: block;
|
||||
border-left: 1px dotted rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.group > .caption .icon.type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.group.balancer > .caption .icon.type {
|
||||
display: inline-block;
|
||||
background-image: url('../images/protocol-icons/guac-monitor.png');
|
||||
}
|
||||
|
||||
.group > .caption .icon.group {
|
||||
opacity: 0.75;
|
||||
background-image: url('../images/group-icons/guac-closed.png');
|
||||
}
|
||||
|
||||
.group.expanded > .caption .icon.group {
|
||||
background-image: url('../images/group-icons/guac-open.png');
|
||||
}
|
||||
|
||||
.group.empty > .caption .icon.group {
|
||||
opacity: 0.25;
|
||||
background-image: url('../images/group-icons/guac-open.png');
|
||||
}
|
||||
|
||||
.group.empty.balancer > .caption .icon.group {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Settings formatting
|
||||
*/
|
||||
|
||||
.form dt,
|
||||
.settings dt {
|
||||
border-bottom: 1px dotted #AAA;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.form dd,
|
||||
.settings dd {
|
||||
margin: 1.5em;
|
||||
margin-left: 2.5em;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
#connections input.name,
|
||||
#users input.name {
|
||||
max-width: 80%;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
#connection-list,
|
||||
#user-list {
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
min-height: 20em;
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
#connections #add-connection,
|
||||
#connections #add-connection-group,
|
||||
#users #add-user {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#connection-add-form,
|
||||
#user-add-form {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
body:not(.manage-connections) .require-manage-connections,
|
||||
body:not(.manage-users) .require-manage-users {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body:not(.add-connections) #add-connection,
|
||||
body:not(.add-connection-groups) #add-connection-group,
|
||||
body:not(.add-users) #user-add-form {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#logout-panel {
|
||||
padding: 0.45em;
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.history th,
|
||||
.history td {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.first-page,
|
||||
.prev-page,
|
||||
.set-page,
|
||||
.next-page,
|
||||
.last-page {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.first-page.disabled,
|
||||
.prev-page.disabled,
|
||||
.set-page.disabled,
|
||||
.next-page.disabled,
|
||||
.last-page.disabled {
|
||||
cursor: auto;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.set-page,
|
||||
.more-pages {
|
||||
display: inline-block;
|
||||
padding: 0.25em;
|
||||
text-align: center;
|
||||
min-width: 1.25em;
|
||||
}
|
||||
|
||||
.set-page {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.set-page.current {
|
||||
cursor: auto;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
-moz-border-radius: 0.2em;
|
||||
-webkit-border-radius: 0.2em;
|
||||
-khtml-border-radius: 0.2em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
.icon.first-page {
|
||||
background-image: url('../images/action-icons/guac-first-page.png');
|
||||
}
|
||||
|
||||
.icon.prev-page {
|
||||
background-image: url('../images/action-icons/guac-prev-page.png');
|
||||
}
|
||||
|
||||
.icon.next-page {
|
||||
background-image: url('../images/action-icons/guac-next-page.png');
|
||||
}
|
||||
|
||||
.icon.last-page {
|
||||
background-image: url('../images/action-icons/guac-last-page.png');
|
||||
}
|
||||
|
||||
.buttons,
|
||||
.list-pager-buttons {
|
||||
text-align: center;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
button#logout {
|
||||
|
||||
background-image: url('../images/action-icons/guac-logout.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1em;
|
||||
background-position: 0.5em 0.45em;
|
||||
|
||||
padding-left: 1.8em;
|
||||
|
||||
}
|
||||
|