Updated to Guacamole 0.9.1, now closes the session correctly
@ -7,7 +7,7 @@
|
|||||||
<groupId>org.openuds.server</groupId>
|
<groupId>org.openuds.server</groupId>
|
||||||
<artifactId>transport</artifactId>
|
<artifactId>transport</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
<version>1.2.1</version>
|
<version>1.5.0</version>
|
||||||
<name>Guacamole Transport</name>
|
<name>Guacamole Transport</name>
|
||||||
<url>http://openuds.org/</url>
|
<url>http://openuds.org/</url>
|
||||||
|
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glyptodon.guacamole</groupId>
|
<groupId>org.glyptodon.guacamole</groupId>
|
||||||
<artifactId>guacamole-common-js</artifactId>
|
<artifactId>guacamole-common-js</artifactId>
|
||||||
<version>0.9.0</version>
|
<version>0.9.1</version>
|
||||||
<type>zip</type>
|
<type>zip</type>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -78,7 +78,7 @@ public class TunnelServlet
|
|||||||
throw new GuacamoleException("Can't access required user credentials");
|
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();
|
GuacamoleClientInformation info = new GuacamoleClientInformation();
|
||||||
info.setOptimalScreenWidth(Integer.parseInt(width));
|
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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE HTML>
|
<!DOCTYPE html>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Guacamole - Clientless Remote Desktop
|
Copyright (C) 2013 Glyptodon LLC
|
||||||
Copyright (C) 2010 Michael Jumper
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
in the Software without restriction, including without limitation the rights
|
||||||
(at your option) any later version.
|
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,
|
The above copyright notice and this permission notice shall be included in
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
all copies or substantial portions of the Software.
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
Adaptation for UDS use (c) 2013 Virtual Cable S.L.U, also distributed under
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
GNU Affero General Public License
|
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
|
||||||
All source code used to build this package can be obtained from http://openuds.org
|
THE SOFTWARE.
|
||||||
Guacamole source code can be obtained from http://guac-dev.org/
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
|
<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"/>
|
<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"/>
|
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||||
|
<title>Guacamole ${project.version}</title>
|
||||||
<title>UDS</title>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
|
||||||
<!-- Display -->
|
<!-- Display -->
|
||||||
<div class="displayOuter">
|
<div class="displayOuter">
|
||||||
<div class="displayMiddle">
|
<div class="displayMiddle">
|
||||||
@ -47,11 +47,92 @@
|
|||||||
</div>
|
</div>
|
||||||
</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"/>
|
<div id="viewportClone"/>
|
||||||
|
|
||||||
<!-- Notification area -->
|
<!-- Notification area -->
|
||||||
<div id="notificationArea"/>
|
<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 -->
|
<!-- Images which should be preloaded -->
|
||||||
<div id="preload">
|
<div id="preload">
|
||||||
<img src="images/action-icons/guac-close.png"/>
|
<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/blob/blob.js"></script>
|
||||||
<script type="text/javascript" src="scripts/lib/filesaver/filesaver.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>
|
<script type="text/javascript" src="guacamole-common-js/all.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<!-- guacamole-default-webapp scripts -->
|
<!-- guacamole-default-webapp scripts -->
|
||||||
<script type="text/javascript" src="scripts/session.js"></script>
|
<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/guac-ui.js"></script>
|
||||||
<script type="text/javascript" src="scripts/client-ui.js"></script>
|
<script type="text/javascript" src="scripts/client-ui.js"></script>
|
||||||
|
|
||||||
<!-- Init -->
|
<!-- Init -->
|
||||||
<script type="text/javascript"> /* <![CDATA[ */
|
<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.onload = function() {
|
||||||
window.setTimeout(function() {
|
window.setTimeout(GuacUI.Client.connect, 10);
|
||||||
// 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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ]]> */ </script>
|
/* ]]> */ </script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Guacamole - Clientless Remote Desktop
|
* Copyright (C) 2013 Glyptodon LLC
|
||||||
* Copyright (C) 2010 Michael Jumper
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* in the Software without restriction, including without limitation the rights
|
||||||
* (at your option) any later version.
|
* 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,
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* all copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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.
|
* Simple HTML input field.
|
||||||
*
|
*
|
||||||
@ -108,7 +111,6 @@ GuacAdmin.Field._HTML_INPUT = function(type) {
|
|||||||
|
|
||||||
GuacAdmin.Field._HTML_INPUT.prototype = new GuacAdmin.Field();
|
GuacAdmin.Field._HTML_INPUT.prototype = new GuacAdmin.Field();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic text field.
|
* A basic text field.
|
||||||
*
|
*
|
||||||
@ -120,6 +122,34 @@ GuacAdmin.Field.TEXT = function() {
|
|||||||
|
|
||||||
GuacAdmin.Field.TEXT.prototype = new GuacAdmin.Field._HTML_INPUT();
|
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.
|
* A basic password field.
|
||||||
@ -132,7 +162,6 @@ GuacAdmin.Field.PASSWORD = function() {
|
|||||||
|
|
||||||
GuacAdmin.Field.PASSWORD.prototype = new GuacAdmin.Field._HTML_INPUT();
|
GuacAdmin.Field.PASSWORD.prototype = new GuacAdmin.Field._HTML_INPUT();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic numeric field, leveraging the new HTML5 field types.
|
* 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();
|
GuacAdmin.Field.NUMERIC.prototype = new GuacAdmin.Field._HTML_INPUT();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple checkbox.
|
* Simple checkbox.
|
||||||
*
|
*
|
||||||
@ -216,7 +244,6 @@ GuacAdmin.Field.ENUM = function(values) {
|
|||||||
|
|
||||||
GuacAdmin.Field.ENUM.prototype = new GuacAdmin.Field();
|
GuacAdmin.Field.ENUM.prototype = new GuacAdmin.Field();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An arbitrary button.
|
* 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
|
* User edit dialog which allows editing of the user's password and connection
|
||||||
* access level.
|
* access level.
|
||||||
@ -619,8 +645,8 @@ GuacAdmin.UserEditor = function(name, parameters) {
|
|||||||
GuacAdmin.reset();
|
GuacAdmin.reset();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -658,8 +684,8 @@ GuacAdmin.UserEditor = function(name, parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alert on failure
|
// Alert on failure
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -816,8 +842,10 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
|||||||
start.textContent = GuacAdmin.formatDate(record.start);
|
start.textContent = GuacAdmin.formatDate(record.start);
|
||||||
if (record.duration !== null)
|
if (record.duration !== null)
|
||||||
duration.textContent = GuacAdmin.formatSeconds(record.duration);
|
duration.textContent = GuacAdmin.formatSeconds(record.duration);
|
||||||
else
|
else if (record.active)
|
||||||
duration.textContent = "Active now";
|
duration.textContent = "Active now";
|
||||||
|
else
|
||||||
|
duration.textContent = "-";
|
||||||
|
|
||||||
// Add record to pager
|
// Add record to pager
|
||||||
history_pager.addElement(row);
|
history_pager.addElement(row);
|
||||||
@ -882,6 +910,11 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
|||||||
field = new GuacAdmin.Field.ENUM(parameter.options);
|
field = new GuacAdmin.Field.ENUM(parameter.options);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Multiline text field
|
||||||
|
case GuacamoleService.Protocol.Parameter.MULTILINE:
|
||||||
|
field = new GuacAdmin.Field.MULTILINE();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -952,8 +985,8 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
|||||||
GuacAdmin.reset();
|
GuacAdmin.reset();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -991,8 +1024,8 @@ GuacAdmin.ConnectionEditor = function(connection, parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alert on failure
|
// Alert on failure
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1145,8 +1178,8 @@ GuacAdmin.ConnectionGroupEditor = function(group, parameters) {
|
|||||||
GuacAdmin.reset();
|
GuacAdmin.reset();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -1184,8 +1217,8 @@ GuacAdmin.ConnectionGroupEditor = function(group, parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alert on failure
|
// Alert on failure
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(status.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1375,8 +1408,8 @@ GuacAdmin.reset = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alert on failure
|
// Alert on failure
|
||||||
catch (e) {
|
catch (status) {
|
||||||
alert(e.message);
|
alert(tatusmessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Guacamole - Clientless Remote Desktop
|
* Copyright (C) 2013 Glyptodon LLC
|
||||||
* Copyright (C) 2010 Michael Jumper
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* in the Software without restriction, including without limitation the rights
|
||||||
* (at your option) any later version.
|
* 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,
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* all copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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 || {};
|
var GuacUI = GuacUI || {};
|
||||||
|
|
||||||
/**
|
|
||||||
* Current session state, including settings.
|
|
||||||
*/
|
|
||||||
GuacUI.sessionState = new GuacamoleSessionState();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new element having the given tagname and CSS class.
|
* 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
|
* 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
|
* that audio be disabled, this object will pretend that audio is not
|
||||||
@ -133,7 +181,7 @@ GuacUI.Audio = new (function() {
|
|||||||
this.supported = [];
|
this.supported = [];
|
||||||
|
|
||||||
// If sound disabled, we're done now.
|
// If sound disabled, we're done now.
|
||||||
if (GuacUI.sessionState.getProperty("disable-sound"))
|
if (GuacamoleSessionStorage.getItem("disable-sound", false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Build array of supported audio formats
|
// 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
|
* A connection UI object which can be easily added to a list of connections
|
||||||
* for sake of display.
|
* for sake of display.
|
||||||
@ -642,7 +493,6 @@ GuacUI.Pager = function(container) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface object which displays the progress of a download, ultimately
|
* Interface object which displays the progress of a download, ultimately
|
||||||
* becoming a download link once complete.
|
* becoming a download link once complete.
|
||||||
@ -701,6 +551,22 @@ GuacUI.Download = function(filename) {
|
|||||||
progress.textContent = text;
|
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.
|
* Removes the progress indicator and replaces it with a download button.
|
||||||
*/
|
*/
|
||||||
@ -709,7 +575,7 @@ GuacUI.Download = function(filename) {
|
|||||||
element.removeChild(progress);
|
element.removeChild(progress);
|
||||||
GuacUI.addClass(element, "complete");
|
GuacUI.addClass(element, "complete");
|
||||||
|
|
||||||
var download = GuacUI.createChildElement(element, "div", "download");
|
var download = GuacUI.createChildElement(element, "button");
|
||||||
download.textContent = "Download";
|
download.textContent = "Download";
|
||||||
download.onclick = function() {
|
download.onclick = function() {
|
||||||
if (guac_download.ondownload)
|
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()
|
* A grouping component. Child elements can be added via the addElement()
|
||||||
* function. By default, groups display as collapsed.
|
* 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) 2013 Glyptodon LLC
|
||||||
* Copyright (C) 2010 Michael Jumper
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* in the Software without restriction, including without limitation the rights
|
||||||
* (at your option) any later version.
|
* 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,
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* all copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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) {
|
GuacamoleService.handleResponse = function(xhr) {
|
||||||
|
|
||||||
// For HTTP Forbidden, just return permission denied
|
// For HTTP Forbidden, just return permission denied
|
||||||
if (xhr.status == 403)
|
if (xhr.status === 403)
|
||||||
throw new Error("Permission denied.");
|
throw new Guacamole.Status(Guacamole.Status.Code.CLIENT_FORBIDDEN, "Permission denied.");
|
||||||
|
|
||||||
// Otherwise, if unsuccessful, throw error with message derived from
|
// Otherwise, if unsuccessful, throw error with message derived from
|
||||||
// response
|
// response
|
||||||
if (xhr.status != 200) {
|
if (xhr.status !== 200) {
|
||||||
|
|
||||||
|
// Retrieve error code
|
||||||
|
var code = parseInt(xhr.getResponseHeader("Guacamole-Status-Code"));
|
||||||
|
|
||||||
// Retrieve error message
|
// Retrieve error message
|
||||||
var message = xhr.getResponseHeader("Guacamole-Error-Message")
|
var message = xhr.getResponseHeader("Guacamole-Error-Message")
|
||||||
|| xhr.statusText;
|
|| xhr.statusText;
|
||||||
|
|
||||||
// Throw error with derived message
|
// 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;
|
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
|
* Collection of service functions which deal with protocols. Each function
|
||||||
* makes an explicit HTTP query to the server, and parses the response.
|
* makes an explicit HTTP query to the server, and parses the response.
|
||||||
@ -1364,6 +1376,11 @@ GuacamoleService.Protocols = {
|
|||||||
parameter.type = GuacamoleService.Protocol.Parameter.ENUM;
|
parameter.type = GuacamoleService.Protocol.Parameter.ENUM;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Multiline text parameter
|
||||||
|
case "multiline":
|
||||||
|
parameter.type = GuacamoleService.Protocol.Parameter.MULTILINE;
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse all options
|
// 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) 2013 Glyptodon LLC
|
||||||
* Copyright (C) 2010 Michael Jumper
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* in the Software without restriction, including without limitation the rights
|
||||||
* (at your option) any later version.
|
* 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,
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* all copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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.
|
* Global storage for Guacamole pages.
|
||||||
* @constructor
|
|
||||||
*/
|
*/
|
||||||
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
|
* @private
|
||||||
|
* @type String
|
||||||
*/
|
*/
|
||||||
var guac_state = this;
|
var stored_json = "{}";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last read state object.
|
* Called whenever an item value changes.
|
||||||
* @private
|
*
|
||||||
|
* @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,
|
* All attached listeners.
|
||||||
* deleted, or new properties.
|
*
|
||||||
|
* @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
|
// Pull current state
|
||||||
var new_state = JSON.parse(localStorage.getItem("GUACAMOLE_STATE") || "{}");
|
var new_json = localStorage.getItem("GUACAMOLE_STATE") || "{}";
|
||||||
|
|
||||||
// Assign new state
|
var new_state = JSON.parse(new_json);
|
||||||
var old_state = state;
|
var old_state = JSON.parse(stored_json);
|
||||||
state = new_state;
|
|
||||||
|
|
||||||
// Check if any values are different
|
// Check if any values are different
|
||||||
for (var name in new_state) {
|
for (var name in new_state) {
|
||||||
|
|
||||||
// If value changed, call handler
|
// If value changed, notify
|
||||||
var old = old_state[name];
|
var old = old_state[name];
|
||||||
if (old != new_state[name]) {
|
if (old !== new_state[name])
|
||||||
|
__notify_changed(name, new_state[name]);
|
||||||
// Call change handler
|
|
||||||
if (guac_state.onchange)
|
|
||||||
guac_state.onchange(state, 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 {onchange} onchange The function to call when an item changes.
|
||||||
* @param value An arbitrary value.
|
|
||||||
*/
|
*/
|
||||||
this.setProperty = function(name, value) {
|
this.addChangeListener = function(onchange) {
|
||||||
state[name] = value;
|
listeners.push(onchange);
|
||||||
localStorage.setItem("GUACAMOLE_STATE", JSON.stringify(state));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
})();
|
||||||
* 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) 2013 Glyptodon LLC
|
||||||
* Copyright (C) 2010 Michael Jumper
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* in the Software without restriction, including without limitation the rights
|
||||||
* (at your option) any later version.
|
* 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,
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* all copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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 {
|
body {
|
||||||
@ -22,6 +25,7 @@ body {
|
|||||||
font-family: FreeSans, Helvetica, Arial, sans-serif;
|
font-family: FreeSans, Helvetica, Arial, sans-serif;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@ -29,8 +33,9 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.software-cursor {
|
.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;
|
overflow: hidden;
|
||||||
|
cursor: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guac-error .software-cursor {
|
.guac-error .software-cursor {
|
||||||
@ -65,27 +70,6 @@ div.dialogMiddle {
|
|||||||
vertical-align: middle;
|
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 {
|
button {
|
||||||
|
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
@ -130,6 +114,11 @@ div.dialog p {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div#main {
|
||||||
|
overflow: auto;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
div.displayOuter {
|
div.displayOuter {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -256,15 +245,63 @@ div#viewportClone {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
@keyframes show-dialog {
|
||||||
text-shadow: 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black;
|
0% {transform: scale(0.75); }
|
||||||
font-size: xx-large;
|
100% {transform: scale(1); }
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.guac-error .status {
|
@-webkit-keyframes show-dialog {
|
||||||
text-shadow: 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black, 0 0 0.25em black;
|
0% {-webkit-transform: scale(0.75); }
|
||||||
color: #D44;
|
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 {
|
p.hint {
|
||||||
@ -297,24 +334,29 @@ p.hint {
|
|||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
|
|
||||||
font-size: 0.9em;
|
font-size: 0.7em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
border: 1px solid rgba(0, 0, 0, 0.75);
|
||||||
background: black;
|
-moz-border-radius: 0.2em;
|
||||||
opacity: 0.9;
|
-webkit-border-radius: 0.2em;
|
||||||
|
-khtml-border-radius: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
background: white;
|
||||||
|
|
||||||
color: white;
|
color: black;
|
||||||
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
overflow: hidden;
|
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 {
|
.notification div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification .title-bar {
|
.notification .title-bar {
|
||||||
@ -322,7 +364,7 @@ p.hint {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
border-bottom: 1px solid white;
|
border-bottom: 1px solid black;
|
||||||
padding-bottom: 0.5em;
|
padding-bottom: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
@ -331,10 +373,6 @@ p.hint {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification .caption {
|
|
||||||
color: silver;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .close {
|
.notification .close {
|
||||||
|
|
||||||
background: url('../images/action-icons/guac-close.png');
|
background: url('../images/action-icons/guac-close.png');
|
||||||
@ -361,6 +399,7 @@ p.hint {
|
|||||||
to {background-position: 64px 0px;}
|
to {background-position: 64px 0px;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification .caption,
|
||||||
.download.notification .caption {
|
.download.notification .caption {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -368,7 +407,15 @@ p.hint {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload.notification .status,
|
||||||
|
.download.notification .status {
|
||||||
|
color: red;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.download.notification .progress,
|
.download.notification .progress,
|
||||||
|
.upload.notification .progress,
|
||||||
.download.notification .download {
|
.download.notification .download {
|
||||||
|
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
@ -377,16 +424,47 @@ p.hint {
|
|||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
|
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
|
-moz-border-radius: 0.2em;
|
||||||
|
-webkit-border-radius: 0.2em;
|
||||||
|
-khtml-border-radius: 0.2em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
float: right;
|
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 {
|
.download.notification .progress {
|
||||||
|
|
||||||
background: #444 url('../images/progress.png');
|
background: #C2C2C2 url('../images/progress.png');
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
-moz-background-size: 16px 16px;
|
-moz-background-size: 16px 16px;
|
||||||
-webkit-background-size: 16px 16px;
|
-webkit-background-size: 16px 16px;
|
||||||
@ -418,3 +496,284 @@ p.hint {
|
|||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|