mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-30 00:23:21 +00:00 
			
		
		
		
	Merge pull request #27 from glyptodon/touch-gestures
GUAC-901: Restore touch gestures
This commit is contained in:
		| @@ -23,4 +23,4 @@ | |||||||
| /** | /** | ||||||
|  * The module for code used to connect to a connection or balancing group. |  * The module for code used to connect to a connection or balancing group. | ||||||
|  */ |  */ | ||||||
| angular.module('client', ['auth', 'history', 'osk', 'rest', 'textInput']); | angular.module('client', ['auth', 'history', 'osk', 'rest', 'textInput', 'touch']); | ||||||
|   | |||||||
| @@ -26,6 +26,30 @@ | |||||||
| angular.module('home').controller('clientController', ['$scope', '$routeParams', '$injector', | angular.module('home').controller('clientController', ['$scope', '$routeParams', '$injector', | ||||||
|         function clientController($scope, $routeParams, $injector) { |         function clientController($scope, $routeParams, $injector) { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The minimum number of pixels a drag gesture must move to result in the | ||||||
|  |      * menu being shown or hidden. | ||||||
|  |      * | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var MENU_DRAG_DELTA = 64; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The maximum X location of the start of a drag gesture for that gesture | ||||||
|  |      * to potentially show the menu. | ||||||
|  |      * | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var MENU_DRAG_MARGIN = 64; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * When showing or hiding the menu via a drag gesture, the maximum number | ||||||
|  |      * of pixels the touch can move vertically and still affect the menu. | ||||||
|  |      *  | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var MENU_DRAG_VERTICAL_TOLERANCE = 10; | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      * In order to open the guacamole menu, we need to hit ctrl-alt-shift. There are |      * In order to open the guacamole menu, we need to hit ctrl-alt-shift. There are | ||||||
|      * several possible keysysms for each key. |      * several possible keysysms for each key. | ||||||
| @@ -201,6 +225,99 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams', | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Hide menu when the user swipes from the right | ||||||
|  |     $scope.menuDrag = function menuDrag(inProgress, startX, startY, currentX, currentY, deltaX, deltaY) { | ||||||
|  |  | ||||||
|  |         if (Math.abs(currentY - startY)  <  MENU_DRAG_VERTICAL_TOLERANCE | ||||||
|  |                   && startX   - currentX >= MENU_DRAG_DELTA) | ||||||
|  |             $scope.menuShown = false; | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Update menu or client based on dragging gestures | ||||||
|  |     $scope.clientDrag = function clientDrag(inProgress, startX, startY, currentX, currentY, deltaX, deltaY) { | ||||||
|  |  | ||||||
|  |         // Show menu if the user swipes from the left | ||||||
|  |         if (startX <= MENU_DRAG_MARGIN) { | ||||||
|  |  | ||||||
|  |             if (Math.abs(currentY - startY) <  MENU_DRAG_VERTICAL_TOLERANCE | ||||||
|  |                       && currentX - startX  >= MENU_DRAG_DELTA) | ||||||
|  |                 $scope.menuShown = true; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Scroll display if absolute mouse is in use | ||||||
|  |         else if ($scope.clientProperties.emulateAbsoluteMouse) { | ||||||
|  |             $scope.clientProperties.scrollLeft -= deltaX; | ||||||
|  |             $scope.clientProperties.scrollTop -= deltaY; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If a pinch gesture is in progress, the scale of the client display when | ||||||
|  |      * the pinch gesture began. | ||||||
|  |      * | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var initialScale = null; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If a pinch gesture is in progress, the X coordinate of the point on the | ||||||
|  |      * client display that was centered within the pinch at the time the | ||||||
|  |      * gesture began. | ||||||
|  |      *  | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var initialCenterX = 0; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If a pinch gesture is in progress, the Y coordinate of the point on the | ||||||
|  |      * client display that was centered within the pinch at the time the | ||||||
|  |      * gesture began. | ||||||
|  |      *  | ||||||
|  |      * @type Number | ||||||
|  |      */ | ||||||
|  |     var initialCenterY = 0; | ||||||
|  |  | ||||||
|  |     // Zoom and pan client via pinch gestures | ||||||
|  |     $scope.clientPinch = function clientPinch(inProgress, startLength, currentLength, centerX, centerY) { | ||||||
|  |  | ||||||
|  |         // Stop gesture if not in progress | ||||||
|  |         if (!inProgress) { | ||||||
|  |             initialScale = null; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Set initial scale if gesture has just started | ||||||
|  |         if (!initialScale) { | ||||||
|  |             initialScale   = $scope.clientProperties.scale; | ||||||
|  |             initialCenterX = (centerX + $scope.clientProperties.scrollLeft) / initialScale; | ||||||
|  |             initialCenterY = (centerY + $scope.clientProperties.scrollTop)  / initialScale; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Determine new scale absolutely | ||||||
|  |         currentScale = initialScale * currentLength / startLength; | ||||||
|  |  | ||||||
|  |         // Fix scale within limits - scroll will be miscalculated otherwise | ||||||
|  |         currentScale = Math.max(currentScale, $scope.clientProperties.minScale); | ||||||
|  |         currentScale = Math.min(currentScale, $scope.clientProperties.maxScale); | ||||||
|  |  | ||||||
|  |         // Update scale based on pinch distance | ||||||
|  |         $scope.autoFit = false; | ||||||
|  |         $scope.clientProperties.autoFit = false; | ||||||
|  |         $scope.clientProperties.scale = currentScale; | ||||||
|  |  | ||||||
|  |         // Scroll display to keep original pinch location centered within current pinch | ||||||
|  |         $scope.clientProperties.scrollLeft = initialCenterX * currentScale - centerX; | ||||||
|  |         $scope.clientProperties.scrollTop  = initialCenterY * currentScale - centerY; | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     // Show/hide UI elements depending on input method |     // Show/hide UI elements depending on input method | ||||||
|     $scope.$watch('inputMethod', function setInputMethod(inputMethod) { |     $scope.$watch('inputMethod', function setInputMethod(inputMethod) { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -318,6 +318,20 @@ angular.module('client').directive('guacClient', [function guacClient() { | |||||||
|                     client.setClipboard(data); |                     client.setClipboard(data); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|  |             /* | ||||||
|  |              * SCROLLING | ||||||
|  |              */ | ||||||
|  |  | ||||||
|  |             $scope.$watch('clientProperties.scrollLeft', function scrollLeftChanged(scrollLeft) { | ||||||
|  |                 main.scrollLeft = scrollLeft; | ||||||
|  |                 $scope.clientProperties.scrollLeft = main.scrollLeft; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             $scope.$watch('clientProperties.scrollTop', function scrollTopChanged(scrollTop) { | ||||||
|  |                 main.scrollTop = scrollTop; | ||||||
|  |                 $scope.clientProperties.scrollTop = main.scrollTop; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|             /* |             /* | ||||||
|              * CONNECT / RECONNECT |              * CONNECT / RECONNECT | ||||||
|              */ |              */ | ||||||
|   | |||||||
| @@ -35,6 +35,7 @@ div.main { | |||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 100%; |     height: 100%; | ||||||
|     position: relative; |     position: relative; | ||||||
|  |     font-size: 0px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .resize-sensor { | .resize-sensor { | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ | |||||||
| <div class="client-view"> | <div class="client-view"> | ||||||
|  |  | ||||||
|     <!-- Central portion of view --> |     <!-- Central portion of view --> | ||||||
|     <div class="client-body"> |     <div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch"> | ||||||
|  |  | ||||||
|         <!-- Client --> |         <!-- Client --> | ||||||
|         <guac-client  |         <guac-client  | ||||||
| @@ -52,7 +52,7 @@ | |||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <!-- Menu --> | <!-- Menu --> | ||||||
| <div ng-class="{open: menuShown}" id="menu"> | <div ng-class="{open: menuShown}" id="menu" guac-touch-drag="menuDrag"> | ||||||
|     <h2>{{'client.clipboard' | translate}}</h2> |     <h2>{{'client.clipboard' | translate}}</h2> | ||||||
|     <div class="content" id="clipboard-settings"> |     <div class="content" id="clipboard-settings"> | ||||||
|         <p class="description">{{'client.copiedText' | translate}}</p> |         <p class="description">{{'client.copiedText' | translate}}</p> | ||||||
| @@ -64,20 +64,20 @@ | |||||||
|  |  | ||||||
|         <!-- No IME --> |         <!-- No IME --> | ||||||
|         <div class="choice"> |         <div class="choice"> | ||||||
|             <label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="none"/> {{'client.none' | translate}}</label> |             <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> |             <p class="caption"><label for="ime-none">{{'client.noneDesc' | translate}}</label></p> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <!-- Text input --> |         <!-- Text input --> | ||||||
|         <div class="choice"> |         <div class="choice"> | ||||||
|             <div class="figure"><label for="ime-text"><img src="images/settings/tablet-keys.png" alt=""/></label></div> |             <div class="figure"><label for="ime-text"><img src="images/settings/tablet-keys.png" alt=""/></label></div> | ||||||
|             <label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="text"/> {{'client.textInput' | translate}}</label> |             <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> |             <p class="caption"><label for="ime-text">{{'client.textInputDesc' | translate}} </label></p> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <!-- Guac OSK --> |         <!-- Guac OSK --> | ||||||
|         <div class="choice"> |         <div class="choice"> | ||||||
|             <label><input name="input-method" ng-change="closeMenu()" ng-model="inputMethod" type="radio" value="osk"/> {{'client.osk' | translate}}</label> |             <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> |             <p class="caption"><label for="ime-osk">{{'client.oskDesc' | translate}}</label></p> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -83,6 +83,22 @@ angular.module('client').factory('ClientProperties', [function defineClientPrope | |||||||
|          */ |          */ | ||||||
|         this.emulateAbsoluteMouse = template.emulateAbsoluteMouse || true; |         this.emulateAbsoluteMouse = template.emulateAbsoluteMouse || true; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The relative Y coordinate of the scroll offset of the display within | ||||||
|  |          * the client element. | ||||||
|  |          *  | ||||||
|  |          * @type Number | ||||||
|  |          */ | ||||||
|  |         this.scrollTop = template.scrollTop || 0; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The relative X coordinate of the scroll offset of the display within | ||||||
|  |          * the client element. | ||||||
|  |          *  | ||||||
|  |          * @type Number | ||||||
|  |          */ | ||||||
|  |         this.scrollLeft = template.scrollLeft || 0; | ||||||
|  |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     return ClientProperties; |     return ClientProperties; | ||||||
|   | |||||||
							
								
								
									
										192
									
								
								guacamole/src/main/webapp/app/touch/directives/guacTouchDrag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								guacamole/src/main/webapp/app/touch/directives/guacTouchDrag.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | |||||||
|  | /* | ||||||
|  |  * 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 handling of drag gestures on a particular element. | ||||||
|  |  */ | ||||||
|  | angular.module('touch').directive('guacTouchDrag', [function guacTouchDrag() { | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         restrict: 'A', | ||||||
|  |  | ||||||
|  |         link: function linkGuacTouchDrag($scope, $element, $attrs) { | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Called during a drag gesture as the user's finger is placed upon | ||||||
|  |              * the element, moves, and is lifted from the element. | ||||||
|  |              * | ||||||
|  |              * @event | ||||||
|  |              * @param {Boolean} inProgress | ||||||
|  |              *     Whether the gesture is currently in progress. This will | ||||||
|  |              *     always be true except when the gesture has ended, at which | ||||||
|  |              *     point one final call will occur with this parameter set to | ||||||
|  |              *     false. | ||||||
|  |              * | ||||||
|  |              * @param {Number} startX | ||||||
|  |              *     The X location at which the drag gesture began. | ||||||
|  |              *      | ||||||
|  |              * @param {Number} startY | ||||||
|  |              *     The Y location at which the drag gesture began. | ||||||
|  |              *      | ||||||
|  |              * @param {Number} currentX | ||||||
|  |              *     The current X location of the user's finger. | ||||||
|  |              *      | ||||||
|  |              * @param {Number} currentY | ||||||
|  |              *     The current Y location of the user's finger. | ||||||
|  |              *      | ||||||
|  |              * @param {Number} deltaX | ||||||
|  |              *     The difference in X location relative to the start of the | ||||||
|  |              *     gesture. | ||||||
|  |              *  | ||||||
|  |              * @param {Number} deltaY | ||||||
|  |              *     The difference in Y location relative to the start of the | ||||||
|  |              *     gesture. | ||||||
|  |              *  | ||||||
|  |              * @return {Boolean} | ||||||
|  |              *     false if the default action of the touch event should be | ||||||
|  |              *     prevented, any other value otherwise. | ||||||
|  |              */ | ||||||
|  |             var guacTouchDrag = $scope.$eval($attrs.guacTouchDrag); | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The element which will register the drag gesture. | ||||||
|  |              * | ||||||
|  |              * @type Element | ||||||
|  |              */ | ||||||
|  |             var element = $element[0]; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Whether a drag gesture is in progress. | ||||||
|  |              *  | ||||||
|  |              * @type Boolean | ||||||
|  |              */ | ||||||
|  |             var inProgress = false; | ||||||
|  |              | ||||||
|  |             /** | ||||||
|  |              * The starting X location of the drag gesture. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var startX = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The starting Y location of the drag gesture. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var startY = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The current X location of the drag gesture. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var currentX = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The current Y location of the drag gesture. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var currentY = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The change in X relative to drag start. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var deltaX = 0; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The change in X relative to drag start. | ||||||
|  |              *  | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var deltaY = 0; | ||||||
|  |  | ||||||
|  |             // When there is exactly one touch, monitor the change in location | ||||||
|  |             element.addEventListener("touchmove", function dragTouchMove(e) { | ||||||
|  |                 if (e.touches.length === 1) { | ||||||
|  |  | ||||||
|  |                     e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                     // Get touch location | ||||||
|  |                     var x = e.touches[0].clientX; | ||||||
|  |                     var y = e.touches[0].clientY; | ||||||
|  |  | ||||||
|  |                     // Init start location and deltas if gesture is starting | ||||||
|  |                     if (!startX || !startY) { | ||||||
|  |                         startX = currentX = x; | ||||||
|  |                         startY = currentY = y; | ||||||
|  |                         deltaX = 0; | ||||||
|  |                         deltaY = 0; | ||||||
|  |                         inProgress = true; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     // Update deltas if gesture is in progress | ||||||
|  |                     else if (inProgress) { | ||||||
|  |                         deltaX = x - currentX; | ||||||
|  |                         deltaY = y - currentY; | ||||||
|  |                         currentX = x; | ||||||
|  |                         currentY = y; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     // Signal start/change in drag gesture | ||||||
|  |                     if (inProgress && guacTouchDrag) { | ||||||
|  |                         $scope.$apply(function dragChanged() { | ||||||
|  |                             if (guacTouchDrag(true, startX, startY, currentX, currentY, deltaX, deltaY) === false) | ||||||
|  |                                 e.preventDefault(); | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |             }, false); | ||||||
|  |  | ||||||
|  |             // Reset monitoring and fire end event when done | ||||||
|  |             element.addEventListener("touchend", function dragTouchEnd(e) { | ||||||
|  |  | ||||||
|  |                 if (startX && startY && e.touches.length === 0) { | ||||||
|  |  | ||||||
|  |                     e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                     // Signal end of drag gesture | ||||||
|  |                     if (inProgress && guacTouchDrag) { | ||||||
|  |                         $scope.$apply(function dragComplete() { | ||||||
|  |                             if (guacTouchDrag(true, startX, startY, currentX, currentY, deltaX, deltaY === false)) | ||||||
|  |                                 e.preventDefault(); | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     startX = currentX = null; | ||||||
|  |                     startY = currentY = null; | ||||||
|  |                     deltaX = 0; | ||||||
|  |                     deltaY = 0; | ||||||
|  |                     inProgress = false; | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             }, false); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  | }]); | ||||||
							
								
								
									
										213
									
								
								guacamole/src/main/webapp/app/touch/directives/guacTouchPinch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								guacamole/src/main/webapp/app/touch/directives/guacTouchPinch.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | |||||||
|  | /* | ||||||
|  |  * 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 handling of pinch gestures (pinch-to-zoom, for | ||||||
|  |  * example) on a particular element. | ||||||
|  |  */ | ||||||
|  | angular.module('touch').directive('guacTouchPinch', [function guacTouchPinch() { | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         restrict: 'A', | ||||||
|  |  | ||||||
|  |         link: function linkGuacTouchPinch($scope, $element, $attrs) { | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Called when a pinch gesture begins, changes, or ends. | ||||||
|  |              * | ||||||
|  |              * @event | ||||||
|  |              * @param {Boolean} inProgress | ||||||
|  |              *     Whether the gesture is currently in progress. This will | ||||||
|  |              *     always be true except when the gesture has ended, at which | ||||||
|  |              *     point one final call will occur with this parameter set to | ||||||
|  |              *     false. | ||||||
|  |              * | ||||||
|  |              * @param {Number} startLength  | ||||||
|  |              *     The initial distance between the two touches of the | ||||||
|  |              *     pinch gesture, in pixels. | ||||||
|  |              * | ||||||
|  |              * @param {Number} currentLength  | ||||||
|  |              *     The current distance between the two touches of the | ||||||
|  |              *     pinch gesture, in pixels. | ||||||
|  |              * | ||||||
|  |              * @param {Number} centerX | ||||||
|  |              *     The current X coordinate of the center of the pinch gesture. | ||||||
|  |              * | ||||||
|  |              * @param {Number} centerY | ||||||
|  |              *     The current Y coordinate of the center of the pinch gesture. | ||||||
|  |              *  | ||||||
|  |              * @return {Boolean} | ||||||
|  |              *     false if the default action of the touch event should be | ||||||
|  |              *     prevented, any other value otherwise. | ||||||
|  |              */ | ||||||
|  |             var guacTouchPinch = $scope.$eval($attrs.guacTouchPinch); | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The element which will register the pinch gesture. | ||||||
|  |              * | ||||||
|  |              * @type Element | ||||||
|  |              */ | ||||||
|  |             var element = $element[0]; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The starting pinch distance, or null if the gesture has not yet | ||||||
|  |              * started. | ||||||
|  |              * | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var startLength = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The current pinch distance, or null if the gesture has not yet | ||||||
|  |              * started. | ||||||
|  |              * | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var currentLength = null; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The X coordinate of the current center of the pinch gesture. | ||||||
|  |              * | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var centerX = 0; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * The Y coordinate of the current center of the pinch gesture. | ||||||
|  |              * @type Number | ||||||
|  |              */ | ||||||
|  |             var centerY = 0; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Given a touch event, calculates the distance between the first | ||||||
|  |              * two touches in pixels. | ||||||
|  |              * | ||||||
|  |              * @param {TouchEvent} e | ||||||
|  |              *     The touch event to use when performing distance calculation. | ||||||
|  |              *  | ||||||
|  |              * @return {Number} | ||||||
|  |              *     The distance in pixels between the first two touches. | ||||||
|  |              */ | ||||||
|  |             var pinchDistance = function pinchDistance(e) { | ||||||
|  |  | ||||||
|  |                 var touchA = e.touches[0]; | ||||||
|  |                 var touchB = e.touches[1]; | ||||||
|  |  | ||||||
|  |                 var deltaX = touchA.clientX - touchB.clientX; | ||||||
|  |                 var deltaY = touchA.clientY - touchB.clientY; | ||||||
|  |  | ||||||
|  |                 return Math.sqrt(deltaX*deltaX + deltaY*deltaY); | ||||||
|  |  | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Given a touch event, calculates the center between the first two | ||||||
|  |              * touches in pixels, returning the X coordinate of this center. | ||||||
|  |              * | ||||||
|  |              * @param {TouchEvent} e | ||||||
|  |              *     The touch event to use when performing center calculation. | ||||||
|  |              *  | ||||||
|  |              * @return {Number} | ||||||
|  |              *     The X coordinate of the center of the first two touches. | ||||||
|  |              */ | ||||||
|  |             var pinchCenterX = function pinchCenterX(e) { | ||||||
|  |  | ||||||
|  |                 var touchA = e.touches[0]; | ||||||
|  |                 var touchB = e.touches[1]; | ||||||
|  |  | ||||||
|  |                 return (touchA.clientX + touchB.clientX) / 2; | ||||||
|  |  | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Given a touch event, calculates the center between the first two | ||||||
|  |              * touches in pixels, returning the Y coordinate of this center. | ||||||
|  |              * | ||||||
|  |              * @param {TouchEvent} e | ||||||
|  |              *     The touch event to use when performing center calculation. | ||||||
|  |              *  | ||||||
|  |              * @return {Number} | ||||||
|  |              *     The Y coordinate of the center of the first two touches. | ||||||
|  |              */ | ||||||
|  |             var pinchCenterY = function pinchCenterY(e) { | ||||||
|  |  | ||||||
|  |                 var touchA = e.touches[0]; | ||||||
|  |                 var touchB = e.touches[1]; | ||||||
|  |  | ||||||
|  |                 return (touchA.clientY + touchB.clientY) / 2; | ||||||
|  |  | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             // When there are exactly two touches, monitor the distance between | ||||||
|  |             // them, firing zoom events as appropriate | ||||||
|  |             element.addEventListener("touchmove", function pinchTouchMove(e) { | ||||||
|  |                 if (e.touches.length === 2) { | ||||||
|  |  | ||||||
|  |                     e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                     // Calculate current zoom level | ||||||
|  |                     currentLength = pinchDistance(e); | ||||||
|  |  | ||||||
|  |                     // Calculate center | ||||||
|  |                     centerX = pinchCenterX(e); | ||||||
|  |                     centerY = pinchCenterY(e); | ||||||
|  |  | ||||||
|  |                     // Init start length if pinch is not in progress | ||||||
|  |                     if (!startLength) | ||||||
|  |                         startLength = currentLength; | ||||||
|  |  | ||||||
|  |                     // Notify of pinch status | ||||||
|  |                     if (guacTouchPinch) { | ||||||
|  |                         $scope.$apply(function pinchChanged() { | ||||||
|  |                             if (guacTouchPinch(true, startLength, currentLength, centerX, centerY) === false) | ||||||
|  |                                 e.preventDefault(); | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |             }, false); | ||||||
|  |  | ||||||
|  |             // Reset monitoring and fire end event when done | ||||||
|  |             element.addEventListener("touchend", function pinchTouchEnd(e) { | ||||||
|  |  | ||||||
|  |                 if (startLength && e.touches.length < 2) { | ||||||
|  |  | ||||||
|  |                     e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                     // Notify of pinch end | ||||||
|  |                     if (guacTouchPinch) { | ||||||
|  |                         $scope.$apply(function pinchComplete() { | ||||||
|  |                             if (guacTouchPinch(false, startLength, currentLength, centerX, centerY) === false) | ||||||
|  |                                 e.preventDefault(); | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     startLength = null; | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             }, false); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  | }]); | ||||||
							
								
								
									
										26
									
								
								guacamole/src/main/webapp/app/touch/touchModule.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								guacamole/src/main/webapp/app/touch/touchModule.js
									
									
									
									
									
										Normal 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 touch gestures, like panning or pinch-to-zoom. | ||||||
|  |  */ | ||||||
|  | angular.module('touch', []); | ||||||
		Reference in New Issue
	
	Block a user