mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-190: Merge client thumbnail tab favicon.
This commit is contained in:
		| @@ -35,6 +35,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|     var clipboardService      = $injector.get('clipboardService'); | ||||
|     var guacClientManager     = $injector.get('guacClientManager'); | ||||
|     var guacNotification      = $injector.get('guacNotification'); | ||||
|     var iconService           = $injector.get('iconService'); | ||||
|     var preferenceService     = $injector.get('preferenceService'); | ||||
|     var tunnelService         = $injector.get('tunnelService'); | ||||
|     var userPageService       = $injector.get('userPageService'); | ||||
| @@ -403,6 +404,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     // Update page icon when thumbnail changes | ||||
|     $scope.$watch('client.thumbnail.canvas', function thumbnailChanged(canvas) { | ||||
|         iconService.setIcons(canvas); | ||||
|     }); | ||||
|  | ||||
|     // Watch clipboard for new data, associating it with any pressed keys | ||||
|     $scope.$watch('client.clipboardData', function clipboardChanged(data) { | ||||
|  | ||||
|   | ||||
| @@ -24,14 +24,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|     function defineManagedClient($rootScope, $injector) { | ||||
|  | ||||
|     // Required types | ||||
|     var ClientProperties     = $injector.get('ClientProperties'); | ||||
|     var ClientIdentifier     = $injector.get('ClientIdentifier'); | ||||
|     var ClipboardData        = $injector.get('ClipboardData'); | ||||
|     var ManagedClientState   = $injector.get('ManagedClientState'); | ||||
|     var ManagedDisplay       = $injector.get('ManagedDisplay'); | ||||
|     var ManagedFilesystem    = $injector.get('ManagedFilesystem'); | ||||
|     var ManagedFileUpload    = $injector.get('ManagedFileUpload'); | ||||
|     var ManagedShareLink     = $injector.get('ManagedShareLink'); | ||||
|     var ClientProperties       = $injector.get('ClientProperties'); | ||||
|     var ClientIdentifier       = $injector.get('ClientIdentifier'); | ||||
|     var ClipboardData          = $injector.get('ClipboardData'); | ||||
|     var ManagedClientState     = $injector.get('ManagedClientState'); | ||||
|     var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail'); | ||||
|     var ManagedDisplay         = $injector.get('ManagedDisplay'); | ||||
|     var ManagedFilesystem      = $injector.get('ManagedFilesystem'); | ||||
|     var ManagedFileUpload      = $injector.get('ManagedFileUpload'); | ||||
|     var ManagedShareLink       = $injector.get('ManagedShareLink'); | ||||
|  | ||||
|     // Required services | ||||
|     var $document              = $injector.get('$document'); | ||||
| @@ -46,7 +47,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|     var guacHistory            = $injector.get('guacHistory'); | ||||
|     var guacImage              = $injector.get('guacImage'); | ||||
|     var guacVideo              = $injector.get('guacVideo'); | ||||
|          | ||||
|  | ||||
|     /** | ||||
|      * The minimum amount of time to wait between updates to the client | ||||
|      * thumbnail, in milliseconds. | ||||
|      * | ||||
|      * @type Number | ||||
|      */ | ||||
|     var THUMBNAIL_UPDATE_FREQUENCY = 5000; | ||||
|  | ||||
|     /** | ||||
|      * Object which serves as a surrogate interface, encapsulating a Guacamole | ||||
|      * client while it is active, allowing it to be detached and reattached | ||||
| @@ -98,6 +107,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|          */ | ||||
|         this.name = template.name; | ||||
|  | ||||
|         /** | ||||
|          * The most recently-generated thumbnail for this connection, as | ||||
|          * stored within the local connection history. If no thumbnail is | ||||
|          * stored, this will be null. | ||||
|          * | ||||
|          * @type ManagedClientThumbnail | ||||
|          */ | ||||
|         this.thumbnail = template.thumbnail; | ||||
|  | ||||
|         /** | ||||
|          * The current clipboard contents. | ||||
|          * | ||||
| @@ -227,45 +245,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Store the thumbnail of the given managed client within the connection | ||||
|      * history under its associated ID. If the client is not connected, this | ||||
|      * function has no effect. | ||||
|      * | ||||
|      * @param {String} managedClient | ||||
|      *     The client whose history entry should be updated. | ||||
|      */ | ||||
|     var updateHistoryEntry = function updateHistoryEntry(managedClient) { | ||||
|  | ||||
|         var display = managedClient.client.getDisplay(); | ||||
|  | ||||
|         // Update stored thumbnail of previous connection  | ||||
|         if (display && display.getWidth() > 0 && display.getHeight() > 0) { | ||||
|  | ||||
|             // Get screenshot | ||||
|             var canvas = display.flatten(); | ||||
|              | ||||
|             // Calculate scale of thumbnail (max 320x240, max zoom 100%) | ||||
|             var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1); | ||||
|              | ||||
|             // Create thumbnail canvas | ||||
|             var thumbnail = $document[0].createElement("canvas"); | ||||
|             thumbnail.width  = canvas.width*scale; | ||||
|             thumbnail.height = canvas.height*scale; | ||||
|              | ||||
|             // Scale screenshot to thumbnail | ||||
|             var context = thumbnail.getContext("2d"); | ||||
|             context.drawImage(canvas, | ||||
|                 0, 0, canvas.width, canvas.height, | ||||
|                 0, 0, thumbnail.width, thumbnail.height | ||||
|             ); | ||||
|  | ||||
|             guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png")); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Requests the creation of a new audio stream, recorded from the user's | ||||
|      * local audio input device. If audio input is supported by the connection, | ||||
| @@ -403,12 +382,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|                         // Begin streaming audio input if possible | ||||
|                         requestAudioStream(client); | ||||
|  | ||||
|                         // Update thumbnail with initial display contents | ||||
|                         ManagedClient.updateThumbnail(managedClient); | ||||
|                         break; | ||||
|  | ||||
|                     // Update history when disconnecting | ||||
|                     case 4: // Disconnecting | ||||
|                     case 5: // Disconnected | ||||
|                         updateHistoryEntry(managedClient); | ||||
|                         ManagedClient.updateThumbnail(managedClient); | ||||
|                         break; | ||||
|  | ||||
|                 } | ||||
| @@ -431,6 +412,21 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|             }); | ||||
|         }; | ||||
|  | ||||
|         // Automatically update the client thumbnail | ||||
|         client.onsync = function syncReceived() { | ||||
|  | ||||
|             var thumbnail = managedClient.thumbnail; | ||||
|             var timestamp = new Date().getTime(); | ||||
|  | ||||
|             // Update thumbnail if it doesn't exist or is old | ||||
|             if (!thumbnail || timestamp - thumbnail.timestamp >= THUMBNAIL_UPDATE_FREQUENCY) { | ||||
|                 $rootScope.$apply(function updateClientThumbnail() { | ||||
|                     ManagedClient.updateThumbnail(managedClient); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|         }; | ||||
|  | ||||
|         // Handle any received clipboard data | ||||
|         client.onclipboard = function clientClipboardReceived(stream, mimetype) { | ||||
|  | ||||
| @@ -651,6 +647,52 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Store the thumbnail of the given managed client within the connection | ||||
|      * history under its associated ID. If the client is not connected, this | ||||
|      * function has no effect. | ||||
|      * | ||||
|      * @param {ManagedClient} managedClient | ||||
|      *     The client whose history entry should be updated. | ||||
|      */ | ||||
|     ManagedClient.updateThumbnail = function updateThumbnail(managedClient) { | ||||
|  | ||||
|         var display = managedClient.client.getDisplay(); | ||||
|  | ||||
|         // Update stored thumbnail of previous connection | ||||
|         if (display && display.getWidth() > 0 && display.getHeight() > 0) { | ||||
|  | ||||
|             // Get screenshot | ||||
|             var canvas = display.flatten(); | ||||
|  | ||||
|             // Calculate scale of thumbnail (max 320x240, max zoom 100%) | ||||
|             var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1); | ||||
|  | ||||
|             // Create thumbnail canvas | ||||
|             var thumbnail = $document[0].createElement("canvas"); | ||||
|             thumbnail.width  = canvas.width*scale; | ||||
|             thumbnail.height = canvas.height*scale; | ||||
|  | ||||
|             // Scale screenshot to thumbnail | ||||
|             var context = thumbnail.getContext("2d"); | ||||
|             context.drawImage(canvas, | ||||
|                 0, 0, canvas.width, canvas.height, | ||||
|                 0, 0, thumbnail.width, thumbnail.height | ||||
|             ); | ||||
|  | ||||
|             // Store updated thumbnail within client | ||||
|             managedClient.thumbnail = new ManagedClientThumbnail({ | ||||
|                 timestamp : new Date().getTime(), | ||||
|                 canvas    : thumbnail | ||||
|             }); | ||||
|  | ||||
|             // Update historical thumbnail | ||||
|             guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png")); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     return ManagedClient; | ||||
|  | ||||
| }]); | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * Licensed to the Apache Software Foundation (ASF) under one | ||||
|  * or more contributor license agreements.  See the NOTICE file | ||||
|  * distributed with this work for additional information | ||||
|  * regarding copyright ownership.  The ASF licenses this file | ||||
|  * to you under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance | ||||
|  * with the License.  You may obtain a copy of the License at | ||||
|  * | ||||
|  *   http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, | ||||
|  * software distributed under the License is distributed on an | ||||
|  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
|  * KIND, either express or implied.  See the License for the | ||||
|  * specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Provides the ManagedClientThumbnail class used by ManagedClient. | ||||
|  */ | ||||
| angular.module('client').factory('ManagedClientThumbnail', [function defineManagedClientThumbnail() { | ||||
|  | ||||
|     /** | ||||
|      * Object which represents a thumbnail of the Guacamole client display, | ||||
|      * along with the time that the thumbnail was generated. | ||||
|      * | ||||
|      * @constructor | ||||
|      * @param {ManagedClientThumbnail|Object} [template={}] | ||||
|      *     The object whose properties should be copied within the new | ||||
|      *     ManagedClientThumbnail. | ||||
|      */ | ||||
|     var ManagedClientThumbnail = function ManagedClientThumbnail(template) { | ||||
|  | ||||
|         // Use empty object by default | ||||
|         template = template || {}; | ||||
|  | ||||
|         /** | ||||
|          * The time that this thumbnail was generated, as the number of | ||||
|          * milliseconds elapsed since midnight of January 1, 1970 UTC. | ||||
|          * | ||||
|          * @type Number | ||||
|          */ | ||||
|         this.timestamp = template.timestamp; | ||||
|  | ||||
|         /** | ||||
|          * The thumbnail of the Guacamole client display. | ||||
|          * | ||||
|          * @type HTMLCanvasElement | ||||
|          */ | ||||
|         this.canvas = template.canvas; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     return ManagedClientThumbnail; | ||||
|  | ||||
| }]); | ||||
							
								
								
									
										148
									
								
								guacamole/src/main/webapp/app/index/services/iconService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								guacamole/src/main/webapp/app/index/services/iconService.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| /* | ||||
|  * Licensed to the Apache Software Foundation (ASF) under one | ||||
|  * or more contributor license agreements.  See the NOTICE file | ||||
|  * distributed with this work for additional information | ||||
|  * regarding copyright ownership.  The ASF licenses this file | ||||
|  * to you under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance | ||||
|  * with the License.  You may obtain a copy of the License at | ||||
|  * | ||||
|  *   http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, | ||||
|  * software distributed under the License is distributed on an | ||||
|  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
|  * KIND, either express or implied.  See the License for the | ||||
|  * specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * A service for updating or resetting the favicon of the current page. | ||||
|  */ | ||||
| angular.module('index').factory('iconService', ['$rootScope', function iconService($rootScope) { | ||||
|  | ||||
|     var service = {}; | ||||
|  | ||||
|     /** | ||||
|      * The URL of the image used for the low-resolution (64x64) favicon. This | ||||
|      * MUST match the URL which is set statically within index.html. | ||||
|      * | ||||
|      * @constant | ||||
|      * @type String | ||||
|      */ | ||||
|     var DEFAULT_SMALL_ICON_URL = 'images/logo-64.png'; | ||||
|  | ||||
|     /** | ||||
|      * The URL of the image used for the high-resolution (144x144) favicon. This | ||||
|      * MUST match the URL which is set statically within index.html. | ||||
|      * | ||||
|      * @constant | ||||
|      * @type String | ||||
|      */ | ||||
|     var DEFAULT_LARGE_ICON_URL = 'images/logo-144.png'; | ||||
|  | ||||
|     /** | ||||
|      * JQuery-wrapped array of all link tags which point to the small, | ||||
|      * low-resolution page icon. | ||||
|      * | ||||
|      * @type Element[] | ||||
|      */ | ||||
|     var smallIcons = $('link[rel=icon][href="' + DEFAULT_SMALL_ICON_URL + '"]'); | ||||
|  | ||||
|     /** | ||||
|      * JQuery-wrapped array of all link tags which point to the large, | ||||
|      * high-resolution page icon. | ||||
|      * | ||||
|      * @type Element[] | ||||
|      */ | ||||
|     var largeIcons = $('link[rel=icon][href="' + DEFAULT_LARGE_ICON_URL + '"]'); | ||||
|  | ||||
|     /** | ||||
|      * Generates an icon by scaling the provided image to fit the given | ||||
|      * dimensions, returning a canvas containing the generated icon. | ||||
|      * | ||||
|      * @param {HTMLCanvasElement} canvas | ||||
|      *     A canvas element containing the image which should be scaled to | ||||
|      *     produce the contents of the generated icon. | ||||
|      * | ||||
|      * @param {Number} width | ||||
|      *     The width of the icon to generate, in pixels. | ||||
|      * | ||||
|      * @param {Number} height | ||||
|      *     The height of the icon to generate, in pixels. | ||||
|      * | ||||
|      * @returns {HTMLCanvasElement} | ||||
|      *     A new canvas element having the given dimensions and containing the | ||||
|      *     provided image, scaled to fit. | ||||
|      */ | ||||
|     var generateIcon = function generateIcon(canvas, width, height) { | ||||
|  | ||||
|         // Create icon canvas having the provided dimensions | ||||
|         var icon = document.createElement('canvas'); | ||||
|         icon.width = width; | ||||
|         icon.height = height; | ||||
|  | ||||
|         // Calculate the scale factor necessary to fit the provided image | ||||
|         // within the icon dimensions | ||||
|         var scale = Math.min(width / canvas.width, height / canvas.height); | ||||
|  | ||||
|         // Calculate the dimensions and position of the scaled image within | ||||
|         // the icon, offsetting the image such that it is centered | ||||
|         var scaledWidth = canvas.width * scale; | ||||
|         var scaledHeight = canvas.height * scale; | ||||
|         var offsetX = (width - scaledWidth) / 2; | ||||
|         var offsetY = (height - scaledHeight) / 2; | ||||
|  | ||||
|         // Draw the icon, scaling the provided image as necessary | ||||
|         var context = icon.getContext('2d'); | ||||
|         context.drawImage(canvas, offsetX, offsetY, scaledWidth, scaledHeight); | ||||
|         return icon; | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Temporarily sets the icon of the current page to the contents of the | ||||
|      * given canvas element. The image within the canvas element will be | ||||
|      * automatically scaled and centered to fit within the dimensions of the | ||||
|      * page icons. The page icons will be automatically reset to their original | ||||
|      * values upon navigation. | ||||
|      * | ||||
|      * @param {HTMLCanvasElement} canvas | ||||
|      *     The canvas element containing the icon. If this value is null or | ||||
|      *     undefined, this function has no effect. | ||||
|      */ | ||||
|     service.setIcons = function setIcons(canvas) { | ||||
|  | ||||
|         // Do nothing if no canvas provided | ||||
|         if (!canvas) | ||||
|             return; | ||||
|  | ||||
|         // Assign low-resolution (64x64) icon | ||||
|         var smallIcon = generateIcon(canvas, 64, 64); | ||||
|         smallIcons.attr('href', smallIcon.toDataURL('image/png')); | ||||
|  | ||||
|         // Assign high-resolution (144x144) icon | ||||
|         var largeIcon = generateIcon(canvas, 144, 144); | ||||
|         largeIcons.attr('href', largeIcon.toDataURL('image/png')); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Resets the icons of the current page to their original values, undoing | ||||
|      * any previous calls to setIcons(). This function is automatically invoked | ||||
|      * upon navigation. | ||||
|      */ | ||||
|     service.setDefaultIcons = function setDefaultIcons() { | ||||
|         smallIcons.attr('href', DEFAULT_SMALL_ICON_URL); | ||||
|         largeIcons.attr('href', DEFAULT_LARGE_ICON_URL); | ||||
|     }; | ||||
|  | ||||
|     // Automatically reset page icons after navigation | ||||
|     $rootScope.$on('$routeChangeSuccess', function resetIcon() { | ||||
|         service.setDefaultIcons(); | ||||
|     }); | ||||
|  | ||||
|     return service; | ||||
|  | ||||
| }]); | ||||
		Reference in New Issue
	
	Block a user