GUAC-968: Reduce number of namespaces. Adopt consistent string naming. Reorganize and reformat. Remove unused strings.

This commit is contained in:
Michael Jumper
2014-12-26 17:00:31 -08:00
parent 83bd541925
commit 74b94f9b2e
22 changed files with 607 additions and 454 deletions

View File

@@ -138,7 +138,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
* showStatus.
*/
var RECONNECT_ACTION = {
name : "client.action.reconnect",
name : "CLIENT.ACTION_RECONNECT",
// Handle reconnect action
callback : function reconnectCallback() {
$scope.id = uniqueId;
@@ -151,7 +151,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
* automatic, timed reconnect.
*/
var RECONNECT_COUNTDOWN = {
text: "client.action.reconnectCountdown",
text: "CLIENT.TEXT_RECONNECT_COUNTDOWN",
callback: RECONNECT_ACTION.callback,
remaining: 15
};
@@ -380,8 +380,8 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
// Show new status if not yet connected
if (status !== "connected") {
$scope.showStatus({
title: "client.status.connectingStatusTitle",
text: "client.status.clientStates." + status
title: "CLIENT.DIALOG_HEADER_CONNECTING",
text: "CLIENT.TEXT_CLIENT_STATUS_" + status.toUpperCase()
});
}
@@ -406,8 +406,8 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
// Show error status
$scope.showStatus({
className: "error",
title: "client.error.connectionErrorTitle",
text: "client.error.clientErrors." + errorName,
title: "CLIENT.DIALOG_HEADER_CONNECTION_ERROR",
text: "CLIENT.ERROR_CLIENT_" + errorName,
countdown: countdown,
actions: [ RECONNECT_ACTION ]
});
@@ -424,8 +424,8 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
$scope.id = null;
$scope.showStatus({
title: "client.status.closedStatusTitle",
text: "client.status.tunnelStates." + status
title: "CLIENT.DIALOG_HEADER_DISCONNECTED",
text: "CLIENT.TEXT_TUNNEL_STATUS_" + status.toUpperCase()
});
}
@@ -446,8 +446,8 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
// Show error status
$scope.showStatus({
className: "error",
title: "client.error.connectionErrorTitle",
text: "client.error.tunnelErrors." + errorName,
title: "CLIENT.DIALOG_HEADER_CONNECTION_ERROR",
text: "CLIENT.ERROR_TUNNEL_" + errorName,
countdown: countdown,
actions: [ RECONNECT_ACTION ]
});
@@ -549,7 +549,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
var notification = {
className : 'download',
title : 'client.fileTransfer.downloadTitle',
title : 'CLIENT.DIALOG_TITLE_FILE_TRANSFER',
text : filename
};
@@ -564,7 +564,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
var notification = downloadNotifications[streamIndex];
if (notification)
notification.progress = getFileProgress('client.fileTransfer.progressText', length);
notification.progress = getFileProgress('CLIENT.TEXT_FILE_TRANSFER_PROGRESS', length);
});
});
@@ -590,7 +590,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
delete notification.progress;
notification.actions = [
{
name : 'client.fileTransfer.save',
name : 'CLIENT.ACTION_SAVE_FILE',
callback : saveFile
}
];
@@ -610,7 +610,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
var notification = {
className : 'upload',
title : 'client.fileTransfer.uploadTitle',
title : 'CLIENT.DIALOG_TITLE_FILE_TRANSFER',
text : filename
};
@@ -625,7 +625,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
var notification = uploadNotifications[streamIndex];
if (notification)
notification.progress = getFileProgress('client.fileTransfer.progressText', offset, length);
notification.progress = getFileProgress('CLIENT.TEXT_FILE_TRANSFER_PROGRESS', offset, length);
});
});
@@ -650,7 +650,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
delete notification.progress;
notification.actions = [
{
name : 'client.fileTransfer.ok',
name : 'CLIENT.ACTION_ACKNOWLEDGE',
callback : closeNotification
}
];
@@ -682,11 +682,11 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
delete notification.progress;
notification.actions = [
{
name : 'client.fileTransfer.ok',
name : 'CLIENT.ACTION_ACKNOWLEDGE',
callback : closeNotification
}
];
notification.text = "client.error.uploadErrors." + errorName;
notification.text = "CLIENT.ERROR_UPLOAD_" + errorName;
notification.className = "upload error";
}

View File

@@ -48,51 +48,51 @@
<!-- On-screen keyboard -->
<div class="keyboard-container" ng-class="{shown: showOSK}">
<guac-osk layout="'client.oskLayout' | translate"/>
<guac-osk layout="'CLIENT.URL_OSK_LAYOUT' | translate"/>
</div>
<!-- Menu -->
<div ng-class="{open: menuShown}" id="menu" guac-touch-drag="menuDrag">
<h2>{{'client.clipboard' | translate}}</h2>
<h2>{{'CLIENT.SECTION_HEADER_CLIPBOARD' | translate}}</h2>
<div class="content" id="clipboard-settings">
<p class="description">{{'client.copiedText' | translate}}</p>
<p class="description">{{'CLIENT.HELP_CLIPBOARD' | translate}}</p>
<textarea ng-model="clipboardData" rows="10" cols="40" id="clipboard"></textarea>
</div>
<h2>{{'client.inputMethod' | translate}}</h2>
<h2>{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}</h2>
<div class="content" id="keyboard-settings">
<!-- No IME -->
<div class="choice">
<label><input id="ime-none" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="none"/> {{'client.none' | translate}}</label>
<p class="caption"><label for="ime-none">{{'client.noneDesc' | translate}}</label></p>
<label><input id="ime-none" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="none"/> {{'CLIENT.NAME_INPUT_METHOD_NONE' | translate}}</label>
<p class="caption"><label for="ime-none">{{'CLIENT.HELP_INPUT_METHOD_NONE' | translate}}</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 id="ime-text" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="text"/> {{'client.textInput' | translate}}</label>
<p class="caption"><label for="ime-text">{{'client.textInputDesc' | translate}} </label></p>
<label><input id="ime-text" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="text"/> {{'CLIENT.NAME_INPUT_METHOD_TEXT' | translate}}</label>
<p class="caption"><label for="ime-text">{{'CLIENT.HELP_INPUT_METHOD_TEXT' | translate}} </label></p>
</div>
<!-- Guac OSK -->
<div class="choice">
<label><input id="ime-osk" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="osk"/> {{'client.osk' | translate}}</label>
<p class="caption"><label for="ime-osk">{{'client.oskDesc' | translate}}</label></p>
<label><input id="ime-osk" name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="osk"/> {{'CLIENT.NAME_INPUT_METHOD_OSK' | translate}}</label>
<p class="caption"><label for="ime-osk">{{'CLIENT.HELP_INPUT_METHOD_OSK' | translate}}</label></p>
</div>
</div>
<h2>{{'client.mouseMode' | translate}}</h2>
<h2>{{'CLIENT.SECTION_HEADER_MOUSE_MODE' | translate}}</h2>
<div class="content" id="mouse-settings">
<p class="description">{{'client.mouseModeDesc' | translate}}</p>
<p class="description">{{'CLIENT.HELP_MOUSE_MODE' | translate}}</p>
<!-- Touchscreen -->
<div class="choice">
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="true" checked="checked" id="absolute"/>
<div class="figure">
<label for="absolute"><img src="images/settings/touchscreen.png" alt="{{'client.touchscreen' | translate}}"/></label>
<p class="caption"><label for="absolute">{{'client.touchscreenDesc' | translate}}</label></p>
<label for="absolute"><img src="images/settings/touchscreen.png" alt="{{'CLIENT.NAME_MOUSE_MODE_ABSOLUTE' | translate}}"/></label>
<p class="caption"><label for="absolute">{{'CLIENT.HELP_MOUSE_MODE_ABSOLUTE' | translate}}</label></p>
</div>
</div>
@@ -100,21 +100,21 @@
<div class="choice">
<input name="mouse-mode" ng-change="closeMenu()" ng-model="clientProperties.emulateAbsoluteMouse" type="radio" ng-value="false" id="relative"/>
<div class="figure">
<label for="relative"><img src="images/settings/touchpad.png" alt="{{'client.touchpad' | translate}}"/></label>
<p class="caption"><label for="relative">{{'client.touchpadDesc' | translate}}</label></p>
<label for="relative"><img src="images/settings/touchpad.png" alt="{{'CLIENT.NAME_MOUSE_MODE_RELATIVE' | translate}}"/></label>
<p class="caption"><label for="relative">{{'CLIENT.HELP_MOUSE_MODE_RELATIVE' | translate}}</label></p>
</div>
</div>
</div>
<h2>{{'client.display' | translate}}</h2>
<h2>{{'CLIENT.SECTION_HEADER_DISPLAY' | translate}}</h2>
<div class="content">
<div id="zoom-settings">
<div ng-click="zoomOut()" id="zoom-out"><img src="images/settings/zoom-out.png" alt="-"/></div>
<div id="zoom-state">{{formattedScale()}}%</div>
<div ng-click="zoomIn()" id="zoom-in"><img src="images/settings/zoom-in.png" alt="+"/></div>
</div>
<div><label><input ng-model="autoFit" ng-change="changeAutoFit()" ng-disabled="autoFitDisabled()" type="checkbox" id="auto-fit"/> {{'client.autoFit' | translate}}</label></div>
<div><label><input ng-model="autoFit" ng-change="changeAutoFit()" ng-disabled="autoFitDisabled()" type="checkbox" id="auto-fit"/> {{'CLIENT.TEXT_ZOOM_AUTO_FIT' | translate}}</label></div>
</div>
</div>

View File

@@ -22,7 +22,7 @@
-->
<!-- Text displayed if no recent connections exist -->
<p class="no-recent" ng-hide="recentConnections.length">{{'home.noRecentConnections' | translate}}</p>
<p class="no-recent" ng-hide="recentConnections.length">{{'HOME.INFO_NO_RECENT_CONNECTIONS' | translate}}</p>
<!-- All recent connections -->
<div ng-repeat="recentConnection in recentConnections" class="connection">

View File

@@ -23,18 +23,18 @@
<div class="connection-list-ui">
<div class="logout-panel">
<a class="manage button" ng-show="currentUserHasUpdate" href="#/manage">{{'home.manage' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
<a class="manage button" ng-show="currentUserHasUpdate" href="#/manage">{{'HOME.ACTION_MANAGE' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'HOME.ACTION_LOGOUT' | translate}}</a>
</div>
<!-- The recent connections for this user -->
<h2>{{'home.recentConnections' | translate}}</h2>
<h2>{{'HOME.SECTION_HEADER_RECENT_CONNECTIONS' | translate}}</h2>
<div class="recent-connections" ng-class="{loading: loading}">
<guac-recent-connections root-group="rootConnectionGroup"/>
</div>
<!-- All connections for this user -->
<h2>{{'home.allConnections' | translate}}</h2>
<h2>{{'HOME.SECTION_HEADER_ALL_CONNECTIONS' | translate}}</h2>
<div class="all-connections" ng-class="{loading: loading}">
<guac-group-list
connection-group="rootConnectionGroup"

View File

@@ -31,37 +31,37 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
$routeProvider
.when('/', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'home',
templateUrl: 'app/home/templates/home.html',
controller: 'homeController'
})
.when('/manage/', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'manage',
templateUrl: 'app/manage/templates/manage.html',
controller: 'manageController'
})
.when('/manage/connections/:id?', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'manage',
templateUrl: 'app/manage/templates/manageConnection.html',
controller: 'manageConnectionController'
})
.when('/manage/connectionGroups/:id?', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'manage',
templateUrl: 'app/manage/templates/manageConnectionGroup.html',
controller: 'manageConnectionGroupController'
})
.when('/manage/users/:id', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'manage',
templateUrl: 'app/manage/templates/manageUser.html',
controller: 'manageUserController'
})
.when('/login/', {
title: 'application.title',
title: 'APP.NAME',
bodyClassName: 'login',
templateUrl: 'app/login/templates/login.html',
controller: 'loginController'

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2014 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.
*/
/**
* Module for handling common localization-related tasks.
*/
angular.module('locale', []);

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2014 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.
*/
/**
* Service for manipulating translation strings and translation table
* identifiers.
*/
angular.module('locale').factory('translationStringService', [function translationStringService() {
var service = {};
/**
* Given an arbitrary identifier, returns the corresponding translation
* table identifier. Translation table identifiers are uppercase strings,
* word components separated by single underscores. For example, the
* string "Swap red/blue" would become "SWAP_RED_BLUE".
*
* @param {String} identifier
* The identifier to transform into a translation table identifier.
*
* @returns {String}
* The translation table identifier.
*/
service.canonicalize = function canonicalize(identifier) {
return identifier.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase();
};
return service;
}]);

View File

@@ -23,7 +23,7 @@ THE SOFTWARE.
<div class="login-ui" ng-class="{error: loginError}" >
<!-- Login error message -->
<p class="login-error">{{'login.loginError' | translate}}</p>
<p class="login-error">{{'LOGIN.ERROR_INVALID_LOGIN' | translate}}</p>
<div class="login-dialog-middle">
@@ -33,17 +33,17 @@ THE SOFTWARE.
<!-- Guacamole version -->
<img class="logo" src="images/guac-tricolor.png" alt=""/>
<div class="version">{{'application.title' | translate}}</div>
<div class="version">{{'APP.NAME' | translate}}</div>
<!-- Login fields (username + password) -->
<div class="login-fields">
<input ng-model="username" placeholder="{{'login.username' | translate}}" type="text" name="username" autofocus="autofocus" class="username"/>
<input ng-model="password" placeholder="{{'login.password' | translate}}" type="password" name="password" class="password"/>
<input ng-model="username" placeholder="{{'LOGIN.FIELD_PLACEHOLDER_USERNAME' | translate}}" type="text" name="username" autofocus="autofocus" class="username"/>
<input ng-model="password" placeholder="{{'LOGIN.FIELD_PLACEHOLDER_PASSWORD' | translate}}" type="password" name="password" class="password"/>
</div>
<!-- Submit button -->
<div class="buttons">
<input type="submit" name="login" class="login" value="{{'login.login' | translate}}"/>
<input type="submit" name="login" class="login" value="{{'LOGIN.ACTION_LOGIN' | translate}}"/>
</div>
</form>

View File

@@ -33,18 +33,19 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
var PermissionSet = $injector.get('PermissionSet');
// Required services
var $location = $injector.get('$location');
var $routeParams = $injector.get('$routeParams');
var connectionService = $injector.get('connectionService');
var connectionGroupService = $injector.get('connectionGroupService');
var protocolService = $injector.get('protocolService');
var $location = $injector.get('$location');
var $routeParams = $injector.get('$routeParams');
var connectionService = $injector.get('connectionService');
var connectionGroupService = $injector.get('connectionGroupService');
var protocolService = $injector.get('protocolService');
var translationStringService = $injector.get('translationStringService');
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "manage.error.action.acknowledge",
name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
$scope.showStatus(false);
@@ -103,6 +104,41 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
$scope.parameters = {};
}
/**
* Given the internal name of a protocol, produces the translation string
* for the localized version of that protocol's name.
*
* @param {String} protocolName
* The name of the protocol.
*
* @returns {String}
* The translation string which produces the localized name of the
* protocol specified.
*/
$scope.getProtocolName = function getProtocolName(protocolName) {
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName) + '.NAME';
};
/**
* Given the internal name of a protocol and the internal name of a
* parameter for that protocol, produces the translation string
* for the localized, human-readable name of that protocol parameter.
*
* @param {String} protocolName
* The name of the protocol.
*
* @param {String} parameterName
* The name of the protocol parameter.
*
* @returns {String}
* The translation string which produces the translated name of the
* protocol parameter specified.
*/
$scope.getProtocolParameterName = function getProtocolParameterName(protocolName, parameterName) {
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName)
+ '.FIELD_HEADER_' + translationStringService.canonicalize(parameterName);
};
/**
* Cancels all pending edits, returning to the management page.
*/
@@ -128,7 +164,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
.error(function connectionSaveFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -141,7 +177,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
* immediately deletes the current connection.
*/
var DELETE_ACTION = {
name : "manage.edit.connection.delete",
name : "MANAGE_CONNECTION.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
@@ -155,7 +191,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "manage.edit.connection.cancel",
name : "MANAGE_CONNECTION.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
$scope.showStatus(false);
@@ -178,7 +214,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
.error(function connectionDeletionFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -194,8 +230,8 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
// Confirm deletion request
$scope.showStatus({
'title' : 'manage.edit.connection.confirmDelete.title',
'text' : 'manage.edit.connection.confirmDelete.text',
'title' : 'MANAGE_CONNECTION.DIALOG_HEADER_CONFIRM_DELETE',
'text' : 'MANAGE_CONNECTION.TEXT_CONFIRM_DELETE',
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});

View File

@@ -40,7 +40,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "manage.error.action.acknowledge",
name : "MANAGE_CONNECTION_GROUP.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
$scope.showStatus(false);
@@ -80,11 +80,11 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
*/
$scope.types = [
{
label: "organizational",
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_ORGANIZATIONAL",
value: ConnectionGroup.Type.ORGANIZATIONAL
},
{
label : "balancing",
label: "MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING",
value : ConnectionGroup.Type.BALANCING
}
];
@@ -112,7 +112,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
.error(function connectionGroupSaveFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_CONNECTION_GROUP.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -125,7 +125,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
* immediately deletes the current connection group.
*/
var DELETE_ACTION = {
name : "manage.edit.connectionGroup.delete",
name : "MANAGE_CONNECTION_GROUP.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
@@ -139,7 +139,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "manage.edit.connectionGroup.cancel",
name : "MANAGE_CONNECTION_GROUP.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
$scope.showStatus(false);
@@ -162,7 +162,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
.error(function connectionGroupDeletionFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_CONNECTION_GROUP.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -178,8 +178,8 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
// Confirm deletion request
$scope.showStatus({
'title' : 'manage.edit.connectionGroup.confirmDelete.title',
'text' : 'manage.edit.connectionGroup.confirmDelete.text',
'title' : 'MANAGE_CONNECTION_GROUP.DIALOG_HEADER_CONFIRM_DELETE',
'text' : 'MANAGE_CONNECTION_GROUP.TEXT_CONFIRM_DELETE',
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});

View File

@@ -40,7 +40,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector',
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "manage.error.action.acknowledge",
name : "MANAGE.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
$scope.showStatus(false);
@@ -90,7 +90,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector',
.error(function userCreationFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});

View File

@@ -43,7 +43,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "manage.error.action.acknowledge",
name : "MANAGE_USER.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
$scope.showStatus(false);
@@ -81,19 +81,19 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
*/
$scope.systemPermissionTypes = [
{
label: "manage.edit.user.administerSystem",
label: "MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
value: PermissionSet.SystemPermissionType.ADMINISTER
},
{
label: "manage.edit.user.createUser",
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
value: PermissionSet.SystemPermissionType.CREATE_USER
},
{
label: "manage.edit.user.createConnection",
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
value: PermissionSet.SystemPermissionType.CREATE_CONNECTION
},
{
label: "manage.edit.user.createConnectionGroup",
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
value: PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP
}
];
@@ -328,8 +328,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
if ($scope.passwordMatch !== $scope.user.password) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'text' : 'manage.edit.user.passwordMismatch',
'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
'text' : 'MANAGE_USER.ERROR_PASSWORD_MISMATCH',
'actions' : [ ACKNOWLEDGE_ACTION ]
});
return;
@@ -349,7 +349,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
.error(function userPermissionsPatchFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -361,7 +361,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
.error(function userSaveFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -374,7 +374,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
* immediately deletes the current user.
*/
var DELETE_ACTION = {
name : "manage.edit.user.delete",
name : "MANAGE_USER.ACTION_DELETE",
className : "danger",
// Handle action
callback : function deleteCallback() {
@@ -388,7 +388,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
* closes the currently-shown status dialog.
*/
var CANCEL_ACTION = {
name : "manage.edit.user.cancel",
name : "MANAGE_USER.ACTION_CANCEL",
// Handle action
callback : function cancelCallback() {
$scope.showStatus(false);
@@ -411,7 +411,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
.error(function userDeletionFailed(error) {
$scope.showStatus({
'className' : 'error',
'title' : 'manage.error.title',
'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR',
'text' : error.message,
'actions' : [ ACKNOWLEDGE_ACTION ]
});
@@ -427,8 +427,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
// Confirm deletion request
$scope.showStatus({
'title' : 'manage.edit.user.confirmDelete.title',
'text' : 'manage.edit.user.confirmDelete.text',
'title' : 'MANAGE_USER.DIALOG_HEADER_CONFIRM_DELETE',
'text' : 'MANAGE_USER.TEXT_CONFIRM_DELETE',
'actions' : [ DELETE_ACTION, CANCEL_ACTION]
});

View File

@@ -57,7 +57,11 @@ angular.module('manage').directive('guacConnectionParameter', [function connecti
},
templateUrl: 'app/manage/templates/connectionParameter.html',
controller: ['$scope', '$q', function connectionParameterController($scope, $q) {
controller: ['$scope', '$injector', function connectionParameterController($scope, $injector) {
// Required services
var $q = $injector.get('$q');
var translationStringService = $injector.get('translationStringService');
/**
* Deferred load of the parameter definition, pending availability
@@ -152,6 +156,31 @@ angular.module('manage').directive('guacConnectionParameter', [function connecti
}); // end watch typedValue
/**
* Given the internal name of a protocol, the internal name of a
* parameter for that protocol, and the internal name for a valid
* value of that parameter, produces the translation string for the
* localized, human-readable name of that parameter value.
*
* @param {String} protocolName
* The name of the protocol.
*
* @param {String} parameterName
* The name of the protocol parameter.
*
* @param {String} parameterValue
* The name of the parameter value.
*
* @returns {String}
* The translation string which produces the translated name of the
* parameter value specified.
*/
$scope.getProtocolParameterOption = function getProtocolParameterOption(protocolName, parameterName, parameterValue) {
return 'PROTOCOL_' + translationStringService.canonicalize(protocolName)
+ '.FIELD_OPTION_' + translationStringService.canonicalize(parameterName)
+ '_' + translationStringService.canonicalize(parameterValue || 'EMPTY');
};
}] // end controller
};

View File

@@ -23,5 +23,5 @@
/**
* The module for the administration functionality.
*/
angular.module('manage', ['btford.modal', 'groupList', 'rest']);
angular.module('manage', ['btford.modal', 'groupList', 'locale', 'rest']);

View File

@@ -26,5 +26,5 @@
<input ng-show="parameter.type === 'USERNAME'" type="text" ng-model="typedValue"/>
<input ng-show="parameter.type === 'PASSWORD'" type="password" ng-model="typedValue"/>
<input ng-show="parameter.type === 'BOOLEAN'" type="checkbox" ng-model="typedValue"/>
<select ng-show="parameter.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as 'protocol.' + protocol.name + '.parameters.' + parameter.name + '.options.' + (option.value || 'empty') | translate for option in parameter.options | orderBy: value"></select>
<select ng-show="parameter.type === 'ENUM'" ng-model="typedValue" ng-options="option.value as getProtocolParameterOption(protocol.name, parameter.name, option.value) | translate for option in parameter.options | orderBy: value"></select>
</span>

View File

@@ -21,22 +21,22 @@ THE SOFTWARE.
-->
<div class="logout-panel">
<a class="back button" href="#/">{{'manage.back' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
<a class="back button" href="#/">{{'MANAGE.ACTION_NAVIGATE_BACK' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'MANAGE.ACTION_LOGOUT' | translate}}</a>
</div>
<h2>{{'manage.administration' | translate}}</h2>
<h2>{{'MANAGE.SECTION_HEADER_ADMINISTRATION' | translate}}</h2>
<div ng-show="currentUserHasUpdate" class="settings section">
<h3 class="require-manage-users">{{'manage.users' | translate}}</h3>
<h3 class="require-manage-users">{{'MANAGE.SECTION_HEADER_USERS' | translate}}</h3>
<div class="require-manage-users users">
<p>{{'manage.usersDescription' | translate}}</p>
<p>{{'MANAGE.HELP_USERS' | translate}}</p>
<!-- Control to create a new user -->
<div class="user-add-form">
<input type="text" ng-model="newUsername" class="name username"/>
<button class="add-user" ng-click="newUser()">{{'manage.addUser' | translate}}</button>
<button class="add-user" ng-click="newUser()">{{'MANAGE.ACTION_NEW_USER' | translate}}</button>
</div>
<!-- List of users this user has access to -->
@@ -50,15 +50,15 @@ THE SOFTWARE.
</div>
</div>
<h3 class="require-manage-connections">{{'manage.connections' | translate}}</h3>
<h3 class="require-manage-connections">{{'MANAGE.SECTION_HEADER_CONNECTIONS' | translate}}</h3>
<div class="require-manage-connections connections">
<p>{{'manage.connectionsDescription' | translate}}</p>
<p>{{'MANAGE.HELP_CONNECTIONS' | translate}}</p>
<!-- Control to create a new connection or group -->
<div class="connection-add-form">
<a class="add-connection button" href="#/manage/connections/">{{'manage.newConnection' | translate}}</a>
<a class="add-connection-group button" href="#/manage/connectionGroups/">{{'manage.newGroup' | translate}}</a>
<a class="add-connection button" href="#/manage/connections/">{{'MANAGE.ACTION_NEW_CONNECTION' | translate}}</a>
<a class="add-connection-group button" href="#/manage/connectionGroups/">{{'MANAGE.ACTION_NEW_CONNECTION_GROUP' | translate}}</a>
</div>
<!-- List of connections and groups this user has access to -->

View File

@@ -21,25 +21,25 @@ THE SOFTWARE.
-->
<div class="logout-panel">
<a class="back button" href="#/manage/">{{'manage.back' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
<a class="back button" href="#/manage/">{{'MANAGE_CONNECTION.ACTION_NAVIGATE_BACK' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'MANAGE_CONNECTION.ACTION_LOGOUT' | translate}}</a>
</div>
<!-- Main property editor -->
<h2>{{'manage.edit.connection.title' | translate}}</h2>
<h2>{{'MANAGE_CONNECTION.SECTION_HEADER_EDIT_CONNECTION' | translate}}</h2>
<div class="section">
<table class="properties">
<!-- Edit connection name -->
<tr>
<th>{{'manage.edit.connection.name' | translate}}</th>
<th>{{'MANAGE_CONNECTION.FIELD_HEADER_NAME' | translate}}</th>
<td><input type="text" ng-model="connection.name"/></td>
</tr>
<!-- Edit connection location -->
<tr>
<th>{{'manage.edit.connection.location' | translate}}</th>
<th>{{'MANAGE_CONNECTION.FIELD_HEADER_LOCATION' | translate}}</th>
<td>
<location-chooser value="connection.parentIdentifier" root-group="rootGroup"></location-chooser>
@@ -49,22 +49,22 @@ THE SOFTWARE.
<!-- Edit connection protocol -->
<tr>
<th>{{'manage.edit.connection.protocol' | translate}}</th>
<th>{{'MANAGE_CONNECTION.FIELD_HEADER_PROTOCOL' | translate}}</th>
<td>
<select ng-model="connection.protocol" ng-options="name as 'protocol.' + protocol.name + '.label' | translate for (name, protocol) in protocols | orderBy: name"></select>
<select ng-model="connection.protocol" ng-options="name as getProtocolName(protocol.name) | translate for (name, protocol) in protocols | orderBy: name"></select>
</td>
</tr>
</table>
</div>
<!-- Connection parameters -->
<h2>{{'manage.edit.connection.parameters' | translate}}</h2>
<h2>{{'MANAGE_CONNECTION.SECTION_HEADER_PARAMETERS' | translate}}</h2>
<div class="section" ng-class="{loading: !parameters}">
<table class="properties">
<!-- All the different possible editable field types -->
<tr ng-repeat="parameter in protocols[connection.protocol].parameters">
<th>{{'protocol.' + connection.protocol + '.parameters.' + parameter.name + '.label' | translate}}:</th>
<th>{{getProtocolParameterName(connection.protocol, parameter.name) | translate}}</th>
<td>
<guac-connection-parameter protocol="protocols[connection.protocol]" name="parameter.name" parameters="parameters"/>
</td>
@@ -74,21 +74,21 @@ THE SOFTWARE.
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-click="saveConnection()">{{'manage.edit.connection.save' | translate}}</button>
<button ng-click="cancel()">{{'manage.edit.connection.cancel' | translate}}</button>
<button ng-click="deleteConnection()" class="danger">{{'manage.edit.connection.delete' | translate}}</button>
<button ng-click="saveConnection()">{{'MANAGE_CONNECTION.ACTION_SAVE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_CONNECTION.ACTION_CANCEL' | translate}}</button>
<button ng-click="deleteConnection()" class="danger">{{'MANAGE_CONNECTION.ACTION_DELETE' | translate}}</button>
</div>
<!-- Connection history -->
<h2>{{'manage.edit.connection.history.usageHistory' | translate}}</h2>
<h2>{{'MANAGE_CONNECTION.SECTION_HEADER_HISTORY' | translate}}</h2>
<div class="history section" ng-class="{loading: !historyEntryWrappers}">
<p ng-hide="historyEntryWrappers.length">{{'manage.edit.connection.history.connectionNotUsed' | translate}}</p>
<p ng-hide="historyEntryWrappers.length">{{'MANAGE_CONNECTION.INFO_CONNECTION_NOT_USED' | translate}}</p>
<table ng-show="historyEntryWrappers.length">
<thead>
<tr>
<th>{{'manage.edit.connection.history.username' | translate}}</th>
<th>{{'manage.edit.connection.history.startTime' | translate}}</th>
<th>{{'manage.edit.connection.history.duration' | translate}}</th>
<th>{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_USERNAME' | translate}}</th>
<th>{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_START' | translate}}</th>
<th>{{'MANAGE_CONNECTION.TABLE_HEADER_HISTORY_DURATION' | translate}}</th>
</tr>
</thead>
<tbody>

View File

@@ -21,25 +21,25 @@ THE SOFTWARE.
-->
<div class="logout-panel">
<a class="back button" href="#/manage/">{{'manage.back' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
<a class="back button" href="#/manage/">{{'MANAGE_CONNECTION_GROUP.ACTION_NAVIGATE_BACK' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'MANAGE_CONNECTION_GROUP.ACTION_LOGOUT' | translate}}</a>
</div>
<!-- Main property editor -->
<h2>{{'manage.edit.connectionGroup.title' | translate}}</h2>
<h2>{{'MANAGE_CONNECTION_GROUP.SECTION_HEADER_EDIT_CONNECTION_GROUP' | translate}}</h2>
<div class="section">
<table class="properties">
<!-- Edit connection group name -->
<tr>
<th>{{'manage.edit.connectionGroup.name' | translate}}</th>
<th>{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_NAME' | translate}}</th>
<td><input type="text" ng-model="connectionGroup.name"/></td>
</tr>
<!-- Edit connection group location -->
<tr>
<th>{{'manage.edit.connectionGroup.location' | translate}}</th>
<th>{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_LOCATION' | translate}}</th>
<td>
<location-chooser value="connectionGroup.parentIdentifier" root-group="rootGroup"/>
@@ -49,9 +49,9 @@ THE SOFTWARE.
<!-- Edit connection group type -->
<tr>
<th>{{'manage.edit.connectionGroup.type.label' | translate}}</th>
<th>{{'MANAGE_CONNECTION_GROUP.FIELD_HEADER_TYPE' | translate}}</th>
<td>
<select ng-model="connectionGroup.type" ng-options="type.value as 'manage.edit.connectionGroup.type.' + type.label | translate for type in types | orderBy: name"></select>
<select ng-model="connectionGroup.type" ng-options="type.value as type.label | translate for type in types | orderBy: name"></select>
</td>
</tr>
</table>
@@ -59,7 +59,7 @@ THE SOFTWARE.
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-click="saveConnectionGroup()">{{'manage.edit.connectionGroup.save' | translate}}</button>
<button ng-click="cancel()">{{'manage.edit.connectionGroup.cancel' | translate}}</button>
<button ng-click="deleteConnectionGroup()" class="danger">{{'manage.edit.connectionGroup.delete' | translate}}</button>
<button ng-click="saveConnectionGroup()">{{'MANAGE_CONNECTION_GROUP.ACTION_SAVE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_CONNECTION_GROUP.ACTION_CANCEL' | translate}}</button>
<button ng-click="deleteConnectionGroup()" class="danger">{{'MANAGE_CONNECTION_GROUP.ACTION_DELETE' | translate}}</button>
</div>

View File

@@ -21,26 +21,26 @@ THE SOFTWARE.
-->
<div class="logout-panel">
<a class="back button" href="#/manage/">{{'manage.back' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
<a class="back button" href="#/manage/">{{'MANAGE_USER.ACTION_NAVIGATE_BACK' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'MANAGE_USER.ACTION_LOGOUT' | translate}}</a>
</div>
<!-- Main property editor -->
<h2>{{'manage.edit.user.title' | translate}}</h2>
<h2>{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}</h2>
<div class="section">
<table class="properties">
<tr>
<th>{{'manage.edit.user.username' | translate}}</th>
<th>{{'MANAGE_USER.FIELD_HEADER_USERNAME' | translate}}</th>
<td>{{user.username}}</td>
</tr>
<tr>
<th>{{'manage.edit.user.password' | translate}}</th>
<th>{{'MANAGE_USER.FIELD_HEADER_PASSWORD' | translate}}</th>
<td><input ng-model="user.password" type="password" /></td>
</tr>
<tr>
<th>{{'manage.edit.user.passwordMatch' | translate}}</th>
<th>{{'MANAGE_USER.FIELD_HEADER_PASSWORD_AGAIN' | translate}}</th>
<td><input ng-model="passwordMatch" type="password" /></td>
</tr>
@@ -48,7 +48,7 @@ THE SOFTWARE.
</div>
<!-- System permissions section -->
<h2>{{'manage.edit.user.permissions' | translate}}</h2>
<h2>{{'MANAGE_USER.SECTION_HEADER_PERMISSIONS' | translate}}</h2>
<div class="section">
<table class="properties">
<tr ng-repeat="systemPermissionType in systemPermissionTypes">
@@ -60,7 +60,7 @@ THE SOFTWARE.
</div>
<!-- Connection and connection group permission section -->
<h2>{{'manage.edit.user.connections' | translate}}</h2>
<h2>{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
<div class="section" ng-class="{loading: !rootGroup}">
<guac-group-list
context="groupListContext"
@@ -71,7 +71,7 @@ THE SOFTWARE.
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-click="saveUser()">{{'manage.edit.user.save' | translate}}</button>
<button ng-click="cancel()">{{'manage.edit.user.cancel' | translate}}</button>
<button ng-click="deleteUser()" class="danger">{{'manage.edit.user.delete' | translate}}</button>
<button ng-click="saveUser()">{{'MANAGE_USER.ACTION_SAVE' | translate}}</button>
<button ng-click="cancel()">{{'MANAGE_USER.ACTION_CANCEL' | translate}}</button>
<button ng-click="deleteUser()" class="danger">{{'MANAGE_USER.ACTION_DELETE' | translate}}</button>
</div>

View File

@@ -59,15 +59,15 @@ angular.module('manage').factory('HistoryEntryWrapper', ['HistoryEntryDuration',
*
* @type String
*/
this.durationText = 'manage.edit.connection.history.formattedDuration';
this.durationText = 'MANAGE_CONNECTION.TEXT_HISTORY_DURATION';
// Notify if connection is active right now
if (historyEntry.active)
this.durationText = 'manage.edit.connection.history.activeNow';
this.durationText = 'MANAGE_CONNECTION.INFO_CONNECTION_ACTIVE_NOW';
// If connection is not active, inform use if end date is not known
else if (!historyEntry.endDate)
this.durationText = 'manage.edit.connection.history.unknownEnd';
this.durationText = 'MANAGE_CONNECTION.INFO_CONNECTION_DURATION_UNKNOWN';
// Set the duration if the necessary information is present
if (historyEntry.endDate && historyEntry.startDate)

View File

@@ -22,6 +22,6 @@
-->
<!-- Text input target -->
<div class="text-input-field"><div class="sent-history"><div class="sent-text" ng-repeat="text in sentText track by $index">{{text}}</div></div><textarea rows="1" class="target"></textarea></div><div class="text-input-buttons"><guac-key keysym="65507" sticky="true" text="'client.ctrl'" pressed="ctrlPressed"></guac-key><guac-key keysym="65513" sticky="true" text="'client.alt'" pressed="altPressed"></guac-key><guac-key keysym="65307" text="'client.esc'"></guac-key><guac-key keysym="65289" text="'client.tab'"></guac-key></div>
<div class="text-input-field"><div class="sent-history"><div class="sent-text" ng-repeat="text in sentText track by $index">{{text}}</div></div><textarea rows="1" class="target"></textarea></div><div class="text-input-buttons"><guac-key keysym="65507" sticky="true" text="'CLIENT.NAME_KEY_CTRL'" pressed="ctrlPressed"></guac-key><guac-key keysym="65513" sticky="true" text="'CLIENT.NAME_KEY_ALT'" pressed="altPressed"></guac-key><guac-key keysym="65307" text="'CLIENT.NAME_KEY_ESC'"></guac-key><guac-key keysym="65289" text="'CLIENT.NAME_KEY_TAB'"></guac-key></div>
</div>