mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	Merge pull request #41 from glyptodon/managed-client
GUAC-963: Restore file transfer support (again)
This commit is contained in:
		| @@ -112,24 +112,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|         0x031D: true | ||||
|     }; | ||||
|   | ||||
|     /** | ||||
|      * All upload error codes handled and passed off for translation. Any error | ||||
|      * code not present in this list will be represented by the "DEFAULT" | ||||
|      * translation. | ||||
|      */ | ||||
|     var UPLOAD_ERRORS = { | ||||
|         0x0100: true, | ||||
|         0x0201: true, | ||||
|         0x0202: true, | ||||
|         0x0203: true, | ||||
|         0x0204: true, | ||||
|         0x0205: true, | ||||
|         0x0301: true, | ||||
|         0x0303: true, | ||||
|         0x0308: true, | ||||
|         0x031D: true | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * All error codes for which automatic reconnection is appropriate when a | ||||
|      * tunnel error occurs. | ||||
| @@ -333,7 +315,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     $scope.$watch('menuShown', function setKeyboardEnabled(menuShown, menuShownPreviousState) { | ||||
|     $scope.$watch('menuShown', function menuVisibilityChanged(menuShown, menuShownPreviousState) { | ||||
|          | ||||
|         // Send clipboard data if menu is hidden | ||||
|         if (!menuShown && menuShownPreviousState) | ||||
| @@ -385,6 +367,17 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|         $scope.page.title = name; | ||||
|     }); | ||||
|  | ||||
|     // Show file transfer section of menu if new file transfers have started | ||||
|     $scope.$watch('client.uploads.length + client.downloads.length', function transfersChanged(count, oldCount) { | ||||
|  | ||||
|         // Show menu and scroll file transfer into view | ||||
|         if (count > oldCount) { | ||||
|             $scope.menuShown = true; | ||||
|             $scope.fileTransferMarker.scrollIntoView(); | ||||
|         } | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     // Show status dialog when connection status changes | ||||
|     $scope.$watch('client.clientState.connectionState', function clientStateChanged(connectionState) { | ||||
|  | ||||
| @@ -505,60 +498,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Returns a progress object, as required by $scope.addNotification(), which | ||||
|      * contains the given number of bytes as an appropriate combination of | ||||
|      * progress value and associated unit. | ||||
|      * | ||||
|      * @param {String} text | ||||
|      *     The translation string to associate with the progress object | ||||
|      *     returned. | ||||
|      * | ||||
|      * @param {Number} bytes The number of bytes. | ||||
|      * @param {Number} [length] The file length, in bytes, if known. | ||||
|      * | ||||
|      * @returns {Object} | ||||
|      *     A progress object, as required by $scope.addNotification(). | ||||
|      */ | ||||
|     var getFileProgress = function getFileProgress(text, bytes, length) { | ||||
|  | ||||
|         // Gigabytes | ||||
|         if (bytes > 1000000000) | ||||
|             return { | ||||
|                 text  : text, | ||||
|                 value : (bytes / 1000000000).toFixed(1), | ||||
|                 ratio : bytes / length, | ||||
|                 unit  : "gb" | ||||
|             }; | ||||
|  | ||||
|         // Megabytes | ||||
|         if (bytes > 1000000) | ||||
|             return { | ||||
|                 text  : text, | ||||
|                 value : (bytes / 1000000).toFixed(1), | ||||
|                 ratio : bytes / length, | ||||
|                 unit  : "mb" | ||||
|             }; | ||||
|  | ||||
|         // Kilobytes | ||||
|         if (bytes > 1000) | ||||
|             return { | ||||
|                 text  : text, | ||||
|                 value : (bytes / 1000).toFixed(1), | ||||
|                 ratio : bytes / length, | ||||
|                 unit  : "kb" | ||||
|             }; | ||||
|  | ||||
|         // Bytes | ||||
|         return { | ||||
|             text  : text, | ||||
|             value : bytes, | ||||
|             ratio : bytes / length, | ||||
|             unit  : "b" | ||||
|         }; | ||||
|  | ||||
|     }; | ||||
|              | ||||
|     // Clean up when view destroyed | ||||
|     $scope.$on('$destroy', function clientViewDestroyed() { | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,237 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Directive which displays an active file transfer, providing links for | ||||
|  * downloads, if applicable. | ||||
|  */ | ||||
| angular.module('client').directive('guacFileTransfer', [function guacFileTransfer() { | ||||
|  | ||||
|     return { | ||||
|         restrict: 'E', | ||||
|         replace: true, | ||||
|         scope: { | ||||
|  | ||||
|             /** | ||||
|              * The file transfer to display. | ||||
|              *  | ||||
|              * @type ManagedFileUpload|ManagedFileDownload | ||||
|              */ | ||||
|             transfer : '=' | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         templateUrl: 'app/client/templates/guacFileTransfer.html', | ||||
|         controller: ['$scope', '$injector', function guacFileTransferController($scope, $injector) { | ||||
|  | ||||
|             // Required types | ||||
|             var ManagedFileTransferState = $injector.get('ManagedFileTransferState'); | ||||
|  | ||||
|             /** | ||||
|              * All upload error codes handled and passed off for translation. | ||||
|              * Any error code not present in this list will be represented by | ||||
|              * the "DEFAULT" translation. | ||||
|              */ | ||||
|             var UPLOAD_ERRORS = { | ||||
|                 0x0100: true, | ||||
|                 0x0201: true, | ||||
|                 0x0202: true, | ||||
|                 0x0203: true, | ||||
|                 0x0204: true, | ||||
|                 0x0205: true, | ||||
|                 0x0301: true, | ||||
|                 0x0303: true, | ||||
|                 0x0308: true, | ||||
|                 0x031D: true | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns the unit string that is most appropriate for the | ||||
|              * number of bytes transferred thus far - either 'gb', 'mb', 'kb', | ||||
|              * or 'b'. | ||||
|              * | ||||
|              * @returns {String} | ||||
|              *     The unit string that is most appropriate for the number of | ||||
|              *     bytes transferred thus far. | ||||
|              */ | ||||
|             $scope.getProgressUnit = function getProgressUnit() { | ||||
|  | ||||
|                 var bytes = $scope.transfer.progress; | ||||
|  | ||||
|                 // Gigabytes | ||||
|                 if (bytes > 1000000000) | ||||
|                     return 'gb'; | ||||
|  | ||||
|                 // Megabytes | ||||
|                 if (bytes > 1000000) | ||||
|                     return 'mb'; | ||||
|  | ||||
|                 // Kilobytes | ||||
|                 if (bytes > 1000) | ||||
|                     return 'kb'; | ||||
|  | ||||
|                 // Bytes | ||||
|                 return 'b'; | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns the amount of data transferred thus far, in the units | ||||
|              * returned by getProgressUnit(). | ||||
|              * | ||||
|              * @returns {Number} | ||||
|              *     The amount of data transferred thus far, in the units | ||||
|              *     returned by getProgressUnit(). | ||||
|              */ | ||||
|             $scope.getProgressValue = function getProgressValue() { | ||||
|  | ||||
|                 var bytes = $scope.transfer.progress; | ||||
|                 if (!bytes) | ||||
|                     return bytes; | ||||
|  | ||||
|                 // Convert bytes to necessary units | ||||
|                 switch ($scope.getProgressUnit()) { | ||||
|  | ||||
|                     // Gigabytes | ||||
|                     case 'gb': | ||||
|                         return (bytes / 1000000000).toFixed(1); | ||||
|  | ||||
|                     // Megabytes | ||||
|                     case 'mb': | ||||
|                         return (bytes / 1000000).toFixed(1); | ||||
|  | ||||
|                     // Kilobytes | ||||
|                     case 'kb': | ||||
|                         return (bytes / 1000).toFixed(1); | ||||
|  | ||||
|                     // Bytes | ||||
|                     case 'b': | ||||
|                     default: | ||||
|                         return bytes; | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns the percentage of bytes transferred thus far, if the | ||||
|              * overall length of the file is known. | ||||
|              * | ||||
|              * @returns {Number} | ||||
|              *     The percentage of bytes transferred thus far, if the | ||||
|              *     overall length of the file is known. | ||||
|              */ | ||||
|             $scope.getPercentDone = function getPercentDone() { | ||||
|                 return $scope.transfer.progress / $scope.transfer.length * 100; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Determines whether the associated file transfer is in progress. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if the file transfer is in progress, false othherwise. | ||||
|              */ | ||||
|             $scope.isInProgress = function isInProgress() { | ||||
|  | ||||
|                 // Not in progress if there is no transfer | ||||
|                 if (!$scope.transfer) | ||||
|                     return false; | ||||
|  | ||||
|                 // Determine in-progress status based on stream state | ||||
|                 switch ($scope.transfer.transferState.streamState) { | ||||
|  | ||||
|                     // IDLE or OPEN file transfers are active | ||||
|                     case ManagedFileTransferState.StreamState.IDLE: | ||||
|                     case ManagedFileTransferState.StreamState.OPEN: | ||||
|                         return true; | ||||
|  | ||||
|                     // All others are not active | ||||
|                     default: | ||||
|                         return false; | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether the file associated with this file transfer can | ||||
|              * be saved locally via a call to save(). | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if a call to save() will result in the file being | ||||
|              *     saved, false otherwise. | ||||
|              */ | ||||
|             $scope.isSavable = function isSavable() { | ||||
|                 return !!$scope.transfer.blob; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Saves the downloaded file, if any. If this transfer is an upload | ||||
|              * or the download is not yet complete, this function has no | ||||
|              * effect. | ||||
|              */ | ||||
|             $scope.save = function save() { | ||||
|  | ||||
|                 // Ignore if no blob exists | ||||
|                 if (!$scope.transfer.blob) | ||||
|                     return; | ||||
|  | ||||
|                 // Save file | ||||
|                 saveAs($scope.transfer.blob, $scope.transfer.filename);  | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns whether an error has occurred. If an error has occurred, | ||||
|              * the transfer is no longer active, and the text of the error can | ||||
|              * be read from getErrorText(). | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if an error has occurred during transfer, false | ||||
|              *     otherwise. | ||||
|              */ | ||||
|             $scope.hasError = function hasError() { | ||||
|                 return $scope.transfer.transferState.streamState === ManagedFileTransferState.StreamState.ERROR; | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Returns the text of the current error as a translation string. | ||||
|              * | ||||
|              * @returns {String} | ||||
|              *     The name of the translation string containing the text | ||||
|              *     associated with the current error. | ||||
|              */ | ||||
|             $scope.getErrorText = function getErrorText() { | ||||
|  | ||||
|                 // Determine translation name of error | ||||
|                 var status = $scope.transfer.transferState.statusCode; | ||||
|                 var errorName = (status in UPLOAD_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT"; | ||||
|  | ||||
|                 // Return translation string | ||||
|                 return 'CLIENT.ERROR_UPLOAD_' + errorName; | ||||
|  | ||||
|             }; | ||||
|  | ||||
|         }] // end file transfer controller | ||||
|  | ||||
|     }; | ||||
| }]); | ||||
| @@ -0,0 +1,137 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Directive which displays all active file transfers. | ||||
|  */ | ||||
| angular.module('client').directive('guacFileTransferManager', [function guacFileTransferManager() { | ||||
|  | ||||
|     return { | ||||
|         restrict: 'E', | ||||
|         replace: true, | ||||
|         scope: { | ||||
|  | ||||
|             /** | ||||
|              * The client whose file transfers should be managed by this | ||||
|              * directive. | ||||
|              *  | ||||
|              * @type ManagerClient | ||||
|              */ | ||||
|             client : '=' | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         templateUrl: 'app/client/templates/guacFileTransferManager.html', | ||||
|         controller: ['$scope', '$injector', function guacFileTransferManagerController($scope, $injector) { | ||||
|  | ||||
|             // Required types | ||||
|             var ManagedClient            = $injector.get('ManagedClient'); | ||||
|             var ManagedFileTransferState = $injector.get('ManagedFileTransferState'); | ||||
|  | ||||
|             /** | ||||
|              * Determines whether the attached client has associated file | ||||
|              * transfers, regardless of those file transfers' state. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if there are any file transfers associated with the | ||||
|              *     attached client, false otherise. | ||||
|              */ | ||||
|             $scope.hasTransfers = function hasTransfers() { | ||||
|  | ||||
|                 // There are no file transfers if there is no client | ||||
|                 if (!$scope.client) | ||||
|                     return false; | ||||
|  | ||||
|                 return !!($scope.client.uploads.length || $scope.client.downloads.length); | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Determines whether the given file transfer state indicates an | ||||
|              * in-progress transfer. | ||||
|              * | ||||
|              * @param {ManagedFileTransferState} transferState | ||||
|              *     The file transfer state to check. | ||||
|              * | ||||
|              * @returns {Boolean} | ||||
|              *     true if the given file transfer state indicates an in- | ||||
|              *     progress transfer, false otherwise. | ||||
|              */ | ||||
|             var isInProgress = function isInProgress(transferState) { | ||||
|                 switch (transferState.streamState) { | ||||
|  | ||||
|                     // IDLE or OPEN file transfers are active | ||||
|                     case ManagedFileTransferState.StreamState.IDLE: | ||||
|                     case ManagedFileTransferState.StreamState.OPEN: | ||||
|                         return true; | ||||
|  | ||||
|                     // All others are not active | ||||
|                     default: | ||||
|                         return false; | ||||
|  | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Removes all file transfers which are not currently in-progress. | ||||
|              */ | ||||
|             $scope.clearCompletedTransfers = function clearCompletedTransfers() { | ||||
|  | ||||
|                 // Nothing to clear if no client attached | ||||
|                 if (!$scope.client) | ||||
|                     return; | ||||
|  | ||||
|                 // Remove completed uploads | ||||
|                 $scope.client.uploads = $scope.client.uploads.filter(function isUploadInProgress(upload) { | ||||
|                     return isInProgress(upload.transferState); | ||||
|                 }); | ||||
|  | ||||
|                 // Remove completed downloads | ||||
|                 $scope.client.downloads = $scope.client.downloads.filter(function isDownloadInProgress(download) { | ||||
|                     return isInProgress(download.transferState); | ||||
|                 }); | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             /** | ||||
|              * Begins a file upload through the attached Guacamole client for | ||||
|              * each file in the given FileList. | ||||
|              * | ||||
|              * @param {FileList} files | ||||
|              *     The files to upload. | ||||
|              */ | ||||
|             $scope.uploadFiles = function uploadFiles(files) { | ||||
|  | ||||
|                 // Ignore file uploads if no attached client | ||||
|                 if (!$scope.client) | ||||
|                     return; | ||||
|  | ||||
|                 // Upload each file  | ||||
|                 for (var i = 0; i < files.length; i++) | ||||
|                     ManagedClient.uploadFile($scope.client, files[i]); | ||||
|  | ||||
|             }; | ||||
|  | ||||
|         }] | ||||
|  | ||||
|     }; | ||||
| }]); | ||||
| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| .transfer-manager .action-buttons { | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .transfer-manager .no-transfers { | ||||
|  | ||||
|     color: rgba(255, 255, 255, 0.5); | ||||
|     text-shadow: -1px -1px rgba(0, 0, 0, 0.5); | ||||
|     opacity: 0.5; | ||||
|      | ||||
|     font-size: 2em; | ||||
|     font-weight: bolder; | ||||
|     text-align: center; | ||||
|  | ||||
| } | ||||
|  | ||||
| .transfer-manager .transfer { | ||||
|     margin: 1em; | ||||
| } | ||||
							
								
								
									
										129
									
								
								guacamole/src/main/webapp/app/client/styles/transfer.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								guacamole/src/main/webapp/app/client/styles/transfer.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| .transfer { | ||||
|     position: relative; | ||||
|     padding: 0.5em; | ||||
|     font-size: 0.75em; | ||||
| } | ||||
|  | ||||
| .transfer .filename { | ||||
|     white-space: nowrap; | ||||
|     text-overflow: ellipsis; | ||||
|     overflow: hidden; | ||||
|     width: 100%; | ||||
|     margin-bottom: 0.5em; | ||||
|     font-family: monospace; | ||||
|     font-weight: bold; | ||||
| } | ||||
|  | ||||
| .transfer .text { | ||||
|     position: relative; | ||||
|     text-align: center; | ||||
|     margin-top: 0.5em; | ||||
| } | ||||
|  | ||||
| @keyframes transfer-progress { | ||||
|     from {background-position: 0px  0px;} | ||||
|     to   {background-position: 64px 0px;} | ||||
| } | ||||
|  | ||||
| @-webkit-keyframes transfer-progress { | ||||
|     from {background-position: 0px  0px;} | ||||
|     to   {background-position: 64px 0px;} | ||||
| } | ||||
|  | ||||
| .transfer .progress { | ||||
|  | ||||
|     width: 100%; | ||||
|     background: #C2C2C2; | ||||
|     padding: 0.25em; | ||||
|      | ||||
|     border: 1px solid gray; | ||||
|  | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     bottom: 0; | ||||
|     opacity: 0.25; | ||||
|      | ||||
| } | ||||
|  | ||||
| .transfer.in-progress .progress { | ||||
|  | ||||
|     background-image: url('images/progress.png'); | ||||
|  | ||||
|     background-size: 16px 16px; | ||||
|     -moz-background-size: 16px 16px; | ||||
|     -webkit-background-size: 16px 16px; | ||||
|     -khtml-background-size: 16px 16px; | ||||
|  | ||||
|     animation-name: transfer-progress; | ||||
|     animation-duration: 2s; | ||||
|     animation-timing-function: linear; | ||||
|     animation-iteration-count: infinite; | ||||
|  | ||||
|     -webkit-animation-name: transfer-progress; | ||||
|     -webkit-animation-duration: 2s; | ||||
|     -webkit-animation-timing-function: linear; | ||||
|     -webkit-animation-iteration-count: infinite; | ||||
|  | ||||
| } | ||||
|  | ||||
| .transfer .progress .bar { | ||||
|     background: #A3D655; | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     height: 100%; | ||||
|     width: 0; | ||||
| } | ||||
|  | ||||
| .savable.transfer { | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .savable.transfer:hover .progress { | ||||
|     border-color: black; | ||||
| } | ||||
|  | ||||
| .savable.transfer .filename { | ||||
|     color: blue; | ||||
|     text-decoration: underline; | ||||
| } | ||||
|  | ||||
| .error.transfer { | ||||
|     background: #FDD; | ||||
| } | ||||
|  | ||||
| .error.transfer .progress { | ||||
|     border-color: rgba(0, 0, 0, 0.125); | ||||
| } | ||||
|  | ||||
| .error.transfer .text, | ||||
| .error.transfer .progress .bar { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| .error-text { | ||||
|     margin-bottom: 0; | ||||
| } | ||||
| @@ -61,12 +61,20 @@ | ||||
|     </div> | ||||
|     <h2>{{client.name}}</h2> | ||||
|  | ||||
|     <!-- Clipboard --> | ||||
|     <h3>{{'CLIENT.SECTION_HEADER_CLIPBOARD' | translate}}</h3> | ||||
|     <div class="content" id="clipboard-settings"> | ||||
|         <p class="description">{{'CLIENT.HELP_CLIPBOARD' | translate}}</p> | ||||
|         <textarea ng-model="client.clipboardData" rows="10" cols="40" id="clipboard"></textarea> | ||||
|     </div> | ||||
|  | ||||
|     <!-- File transfers --> | ||||
|     <h3 guac-marker="fileTransferMarker">{{'CLIENT.SECTION_HEADER_FILE_TRANSFERS' | translate}}</h3> | ||||
|     <div class="content" id="file-transfers"> | ||||
|         <guac-file-transfer-manager client="client"></guac-file-transfer-manager> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Input method --> | ||||
|     <h3>{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}</h3> | ||||
|     <div class="content" id="keyboard-settings"> | ||||
|  | ||||
| @@ -91,6 +99,7 @@ | ||||
|  | ||||
|     </div> | ||||
|  | ||||
|     <!-- Mouse mode --> | ||||
|     <h3>{{'CLIENT.SECTION_HEADER_MOUSE_MODE' | translate}}</h3> | ||||
|     <div class="content" id="mouse-settings"> | ||||
|         <p class="description">{{'CLIENT.HELP_MOUSE_MODE' | translate}}</p> | ||||
| @@ -115,6 +124,7 @@ | ||||
|  | ||||
|     </div> | ||||
|  | ||||
|     <!-- Display options --> | ||||
|     <h3>{{'CLIENT.SECTION_HEADER_DISPLAY' | translate}}</h3> | ||||
|     <div class="content"> | ||||
|         <div id="zoom-settings"> | ||||
| @@ -126,9 +136,3 @@ | ||||
|     </div> | ||||
|  | ||||
| </div> | ||||
|  | ||||
| <!-- Images which should be preloaded --> | ||||
| <div id="preload"> | ||||
|     <img src="images/action-icons/guac-close.png" alt=""/> | ||||
|     <img src="images/progress.png" alt=""/> | ||||
| </div> | ||||
|   | ||||
| @@ -0,0 +1,36 @@ | ||||
| <div class="transfer" ng-class="{'in-progress': isInProgress(), 'savable': isSavable(), 'error': hasError()}" ng-click="save()"> | ||||
|     <!-- | ||||
|        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. | ||||
|     --> | ||||
|  | ||||
|     <!-- Filename --> | ||||
|     <div class="filename">{{transfer.filename}}</div> | ||||
|  | ||||
|     <!-- Progress/status text --> | ||||
|     <div class="text">{{'CLIENT.TEXT_FILE_TRANSFER_PROGRESS' | translate:'{PROGRESS: getProgressValue(), UNIT: getProgressUnit()}'}}</div> | ||||
|  | ||||
|     <!-- Progress bar --> | ||||
|     <div class="progress"><div ng-style="{'width': getPercentDone() + '%'}" class="bar"></div></div> | ||||
|  | ||||
|     <!-- Error text --> | ||||
|     <p class="error-text" ng-show="hasError()">{{getErrorText() | translate}}</p> | ||||
|      | ||||
| </div> | ||||
| @@ -0,0 +1,43 @@ | ||||
| <div class="transfer-manager"> | ||||
|     <!-- | ||||
|        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. | ||||
|     --> | ||||
|  | ||||
|     <!-- No transfers currently present --> | ||||
|     <p class="no-transfers" ng-hide="hasTransfers()">{{'CLIENT.INFO_NO_FILE_TRANSFERS' | translate}}</p> | ||||
|  | ||||
|     <!-- Sent files --> | ||||
|     <div ng-repeat="upload in client.uploads"> | ||||
|         <guac-file-transfer transfer="upload"></guac-file-transfer> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Received files --> | ||||
|     <div ng-repeat="download in client.downloads"> | ||||
|         <guac-file-transfer transfer="download"></guac-file-transfer> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Form buttons --> | ||||
|     <div class="action-buttons"> | ||||
|         <a class="upload button" guac-upload="uploadFiles">{{'CLIENT.ACTION_UPLOAD_FILES' | translate}}</a> | ||||
|         <a class="button" ng-click="clearCompletedTransfers()">{{'CLIENT.ACTION_CLEAR_COMPLETED_TRANSFERS' | translate}}</a> | ||||
|     </div> | ||||
|  | ||||
| </div> | ||||
| @@ -99,7 +99,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|          * | ||||
|          * @type String | ||||
|          */ | ||||
|         this.clipboardData = template.clipboardData; | ||||
|         this.clipboardData = template.clipboardData || ''; | ||||
|  | ||||
|         /** | ||||
|          * All downloaded files. As files are downloaded, their progress can be | ||||
|   | ||||
| @@ -39,7 +39,7 @@ angular.module('element').directive('guacFocus', ['$parse', function guacFocus($ | ||||
|             var guacFocus = $parse($attrs.guacFocus); | ||||
|  | ||||
|             /** | ||||
|              * The element which will register the drag gesture. | ||||
|              * The element which will be focused / blurred. | ||||
|              * | ||||
|              * @type Element | ||||
|              */ | ||||
|   | ||||
| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * A directive which stores a marker which refers to a specific element, | ||||
|  * allowing that element to be scrolled into view when desired. | ||||
|  */ | ||||
| angular.module('element').directive('guacMarker', ['$injector', function guacMarker($injector) { | ||||
|  | ||||
|     // Required types | ||||
|     var Marker = $injector.get('Marker'); | ||||
|  | ||||
|     // Required services | ||||
|     var $parse = $injector.get('$parse'); | ||||
|  | ||||
|     return { | ||||
|         restrict: 'A', | ||||
|  | ||||
|         link: function linkGuacMarker($scope, $element, $attrs) { | ||||
|  | ||||
|             /** | ||||
|              * The property in which a new Marker should be stored. The new | ||||
|              * Marker will refer to the element associated with this directive. | ||||
|              * | ||||
|              * @type Marker | ||||
|              */ | ||||
|             var guacMarker = $parse($attrs.guacMarker); | ||||
|  | ||||
|             /** | ||||
|              * The element to associate with the new Marker. | ||||
|              * | ||||
|              * @type Element | ||||
|              */ | ||||
|             var element = $element[0]; | ||||
|  | ||||
|             // Assign new marker | ||||
|             guacMarker.assign($scope, new Marker(element)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|  | ||||
| }]); | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * A directive which allows multiple files to be uploaded. Clicking on the | ||||
|  * associated element will result in a file selector dialog, which then calls | ||||
|  * the provided callback function with any chosen files. | ||||
|  */ | ||||
| angular.module('element').directive('guacUpload', ['$document', function guacUpload($document) { | ||||
|  | ||||
|     return { | ||||
|         restrict: 'A', | ||||
|  | ||||
|         link: function linkGuacUpload($scope, $element, $attrs) { | ||||
|  | ||||
|             /** | ||||
|              * The function to call whenever files are chosen. The callback is | ||||
|              * provided a single parameter: the FileList containing all chosen | ||||
|              * files. | ||||
|              * | ||||
|              * @type Function  | ||||
|              */ | ||||
|             var guacUpload = $scope.$eval($attrs.guacUpload); | ||||
|  | ||||
|             /** | ||||
|              * The element which will register the drag gesture. | ||||
|              * | ||||
|              * @type Element | ||||
|              */ | ||||
|             var element = $element[0]; | ||||
|  | ||||
|             /** | ||||
|              * Internal form, containing a single file input element. | ||||
|              * | ||||
|              * @type HTMLFormElement | ||||
|              */ | ||||
|             var form = $document[0].createElement('form'); | ||||
|  | ||||
|             /** | ||||
|              * Internal file input element. | ||||
|              * | ||||
|              * @type HTMLInputElement | ||||
|              */ | ||||
|             var input = $document[0].createElement('input'); | ||||
|  | ||||
|             // Init input element | ||||
|             input.type = 'file'; | ||||
|             input.multiple = true; | ||||
|  | ||||
|             // Add input element to internal form | ||||
|             form.appendChild(input); | ||||
|  | ||||
|             // Notify of any chosen files | ||||
|             input.addEventListener('change', function filesSelected() { | ||||
|                 $scope.$apply(function setSelectedFiles() { | ||||
|  | ||||
|                     // Only set chosen files selection is not canceled | ||||
|                     if (guacUpload && input.files.length > 0) | ||||
|                         guacUpload(input.files); | ||||
|  | ||||
|                     // Reset selection | ||||
|                     form.reset(); | ||||
|  | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             // Open file chooser when element is clicked | ||||
|             element.addEventListener('click', function elementClicked() { | ||||
|                 input.click(); | ||||
|             }); | ||||
|  | ||||
|         } // end guacUpload link function | ||||
|  | ||||
|     }; | ||||
|  | ||||
| }]); | ||||
							
								
								
									
										50
									
								
								guacamole/src/main/webapp/app/element/types/Marker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								guacamole/src/main/webapp/app/element/types/Marker.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Provides the Marker class definition. | ||||
|  */ | ||||
| angular.module('element').factory('Marker', [function defineMarker() { | ||||
|  | ||||
|     /** | ||||
|      * Creates a new Marker which allows its associated element to be scolled | ||||
|      * into view as desired. | ||||
|      * | ||||
|      * @constructor | ||||
|      * @param {Element} element | ||||
|      *     The element to associate with this marker. | ||||
|      */ | ||||
|     var Marker = function Marker(element) { | ||||
|  | ||||
|         /** | ||||
|          * Scrolls scrollable elements, or the window, as needed to bring the | ||||
|          * element associated with this marker into view. | ||||
|          */ | ||||
|         this.scrollIntoView = function scrollIntoView() { | ||||
|             element.scrollIntoView(); | ||||
|         }; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     return Marker; | ||||
|  | ||||
| }]); | ||||
| @@ -17,16 +17,17 @@ | ||||
|  | ||||
|     "CLIENT" : { | ||||
|  | ||||
|         "ACTION_RECONNECT"     : "Reconnect", | ||||
|         "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE", | ||||
|         "ACTION_DISCONNECT"    : "Disconnect", | ||||
|         "ACTION_NAVIGATE_BACK" : "@:APP.ACTION_NAVIGATE_BACK", | ||||
|         "ACTION_SAVE_FILE"     : "@:APP.ACTION_SAVE", | ||||
|         "ACTION_ACKNOWLEDGE"               : "@:APP.ACTION_ACKNOWLEDGE", | ||||
|         "ACTION_CLEAR_COMPLETED_TRANSFERS" : "Clear Completed Transfers", | ||||
|         "ACTION_DISCONNECT"                : "Disconnect", | ||||
|         "ACTION_NAVIGATE_BACK"             : "@:APP.ACTION_NAVIGATE_BACK", | ||||
|         "ACTION_RECONNECT"                 : "Reconnect", | ||||
|         "ACTION_SAVE_FILE"                 : "@:APP.ACTION_SAVE", | ||||
|         "ACTION_UPLOAD_FILES"              : "Upload Files", | ||||
|  | ||||
|         "DIALOG_HEADER_CONNECTING"       : "Connecting", | ||||
|         "DIALOG_HEADER_CONNECTION_ERROR" : "Connection Error", | ||||
|         "DIALOG_HEADER_DISCONNECTED"     : "Disconnected", | ||||
|         "DIALOG_HEADER_FILE_TRANSFER"    : "File Transfer", | ||||
|  | ||||
|         "ERROR_CLIENT_201"     : "This connection has been closed because the server is busy. Please wait a few minutes and try again.", | ||||
|         "ERROR_CLIENT_202"     : "The Guacamole server has closed the connection because the remote desktop is taking too long to respond. Please try again or contact your system administrator.", | ||||
| @@ -69,6 +70,8 @@ | ||||
|         "HELP_MOUSE_MODE_ABSOLUTE" : "Tap to click. The click occurs at the location of the touch.", | ||||
|         "HELP_MOUSE_MODE_RELATIVE" : "Drag to move the mouse pointer and tap to click. The click occurs at the location of the pointer.", | ||||
|  | ||||
|         "INFO_NO_FILE_TRANSFERS" : "No file transfers.", | ||||
|  | ||||
|         "NAME_INPUT_METHOD_NONE"   : "None", | ||||
|         "NAME_INPUT_METHOD_OSK"    : "On-screen keyboard", | ||||
|         "NAME_INPUT_METHOD_TEXT"   : "Text input", | ||||
| @@ -79,10 +82,11 @@ | ||||
|         "NAME_MOUSE_MODE_ABSOLUTE" : "Touchscreen", | ||||
|         "NAME_MOUSE_MODE_RELATIVE" : "Touchpad", | ||||
|  | ||||
|         "SECTION_HEADER_CLIPBOARD"    : "Clipboard", | ||||
|         "SECTION_HEADER_INPUT_METHOD" : "Input method", | ||||
|         "SECTION_HEADER_DISPLAY"      : "Display", | ||||
|         "SECTION_HEADER_MOUSE_MODE"   : "Mouse emulation mode", | ||||
|         "SECTION_HEADER_CLIPBOARD"      : "Clipboard", | ||||
|         "SECTION_HEADER_DISPLAY"        : "Display", | ||||
|         "SECTION_HEADER_FILE_TRANSFERS" : "File Transfers", | ||||
|         "SECTION_HEADER_INPUT_METHOD"   : "Input method", | ||||
|         "SECTION_HEADER_MOUSE_MODE"     : "Mouse emulation mode", | ||||
|  | ||||
|         "TEXT_ZOOM_AUTO_FIT"              : "Automatically fit to browser window", | ||||
|         "TEXT_CLIENT_STATUS_IDLE"         : "Idle.", | ||||
| @@ -101,10 +105,10 @@ | ||||
|         "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", | ||||
|         "ACTION_MANAGE" : "@:APP.ACTION_MANAGE", | ||||
|  | ||||
|         "INFO_NO_RECENT_CONNECTIONS" : "No recent Connections.", | ||||
|         "INFO_NO_RECENT_CONNECTIONS" : "No recent connections.", | ||||
|  | ||||
|         "SECTION_HEADER_RECENT_CONNECTIONS" : "Recent Connections", | ||||
|         "SECTION_HEADER_ALL_CONNECTIONS"    : "All Connections" | ||||
|         "SECTION_HEADER_ALL_CONNECTIONS"    : "All Connections", | ||||
|         "SECTION_HEADER_RECENT_CONNECTIONS" : "Recent Connections" | ||||
|  | ||||
|     }, | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user