{{'CLIENT.HELP_CLIPBOARD' | translate}}
+ +diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index b613838e4..05ef4c9e2 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -154,25 +154,49 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
callback: RECONNECT_ACTION.callback,
remaining: 15
};
-
- // Hide menu by default
- $scope.menuShown = false;
- // Use physical keyboard by default
- $scope.inputMethod = 'none';
+ /**
+ * Menu-specific properties.
+ */
+ $scope.menu = {
+
+ /**
+ * Whether the menu is currently shown.
+ *
+ * @type Boolean
+ */
+ shown : false,
+
+ /**
+ * Whether the Guacamole display should be scaled to fit the browser
+ * window.
+ *
+ * @type Boolean
+ */
+ autoFit : true,
+
+ /**
+ * The currently selected input method. This may be either "none",
+ * "osk", or "text".
+ *
+ * @type String
+ */
+ inputMethod : 'none',
+
+ /**
+ * The current scroll state of the menu.
+ *
+ * @type ScrollState
+ */
+ scrollState : new ScrollState()
+
+ };
// Convenience method for closing the menu
$scope.closeMenu = function closeMenu() {
- $scope.menuShown = false;
+ $scope.menu.shown = false;
};
- /**
- * The current scroll state of the menu.
- *
- * @type ScrollState
- */
- $scope.menuScrollState = new ScrollState();
-
// Update the model when clipboard data received from client
$scope.$on('guacClientClipboard', function clientClipboardListener(event, client, mimetype, clipboardData) {
$scope.clipboardData = clipboardData;
@@ -206,12 +230,12 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
// Hide menu if swipe gesture is detected
if (Math.abs(currentY - startY) < MENU_DRAG_VERTICAL_TOLERANCE
&& startX - currentX >= MENU_DRAG_DELTA)
- $scope.menuShown = false;
+ $scope.menu.shown = false;
// Scroll menu by default
else {
- $scope.menuScrollState.left -= deltaX;
- $scope.menuScrollState.top -= deltaY;
+ $scope.menu.scrollState.left -= deltaX;
+ $scope.menu.scrollState.top -= deltaY;
}
return false;
@@ -226,7 +250,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
if (Math.abs(currentY - startY) < MENU_DRAG_VERTICAL_TOLERANCE
&& currentX - startX >= MENU_DRAG_DELTA)
- $scope.menuShown = true;
+ $scope.menu.shown = true;
}
@@ -294,7 +318,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
currentScale = Math.min(currentScale, $scope.client.clientProperties.maxScale);
// Update scale based on pinch distance
- $scope.autoFit = false;
+ $scope.menu.autoFit = false;
$scope.client.clientProperties.autoFit = false;
$scope.client.clientProperties.scale = currentScale;
@@ -307,7 +331,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
};
// Show/hide UI elements depending on input method
- $scope.$watch('inputMethod', function setInputMethod(inputMethod) {
+ $scope.$watch('menu.inputMethod', function setInputMethod(inputMethod) {
// Show input methods only if selected
$scope.showOSK = (inputMethod === 'osk');
@@ -315,7 +339,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
});
- $scope.$watch('menuShown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
+ $scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
// Send clipboard data if menu is hidden
if (!menuShown && menuShownPreviousState)
@@ -351,7 +375,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
// Toggle the menu
$scope.$apply(function() {
- $scope.menuShown = !$scope.menuShown;
+ $scope.menu.shown = !$scope.menu.shown;
});
}
}
@@ -372,8 +396,8 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
// Show menu and scroll file transfer into view
if (count > oldCount) {
- $scope.menuShown = true;
- $scope.fileTransferMarker.scrollIntoView();
+ $scope.menu.shown = true;
+ $scope.menu.fileTransferMarker.scrollIntoView();
}
});
@@ -460,7 +484,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
};
$scope.zoomIn = function zoomIn() {
- $scope.autoFit = false;
+ $scope.menu.autoFit = false;
$scope.client.clientProperties.autoFit = false;
$scope.client.clientProperties.scale += 0.1;
};
@@ -470,12 +494,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
$scope.client.clientProperties.scale -= 0.1;
};
- $scope.autoFit = true;
-
$scope.changeAutoFit = function changeAutoFit() {
- if ($scope.autoFit && $scope.client.clientProperties.minScale) {
+ if ($scope.menu.autoFit && $scope.client.clientProperties.minScale) {
$scope.client.clientProperties.autoFit = true;
- } else {
+ }
+ else {
$scope.client.clientProperties.autoFit = false;
$scope.client.clientProperties.scale = 1;
}
@@ -495,7 +518,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
$scope.client.client.disconnect();
// Hide menu
- $scope.menuShown = false;
+ $scope.menu.shown = false;
};
diff --git a/guacamole/src/main/webapp/app/client/services/guacAudio.js b/guacamole/src/main/webapp/app/client/services/guacAudio.js
index f648ecbb4..061820584 100644
--- a/guacamole/src/main/webapp/app/client/services/guacAudio.js
+++ b/guacamole/src/main/webapp/app/client/services/guacAudio.js
@@ -30,6 +30,11 @@ angular.module('client').factory('guacAudio', [function guacAudio() {
*/
return new (function() {
+ /**
+ * Array of codecs to test.
+ *
+ * @type String[]
+ */
var codecs = [
'audio/ogg; codecs="vorbis"',
'audio/mp4; codecs="mp4a.40.5"',
@@ -38,41 +43,70 @@ angular.module('client').factory('guacAudio', [function guacAudio() {
'audio/wav; codecs=1'
];
+ /**
+ * Array of all codecs that are reported as "probably" supported.
+ *
+ * @type String[]
+ */
var probably_supported = [];
+
+ /**
+ * Array of all codecs that are reported as "maybe" supported.
+ *
+ * @type String[]
+ */
var maybe_supported = [];
+ /**
+ * Internal audio element for the sake of testing codec support. If
+ * audio is explicitly not supported by the browser, this will instead
+ * be null.
+ *
+ * @type Audio
+ */
+ var audio = null;
+
+ // Attempt to create audio element
+ try {
+ audio = new Audio();
+ }
+ catch (e) {
+ // If creation fails, allow audio to remain null
+ }
+
/**
* Array of all supported audio mimetypes, ordered by liklihood of
* working.
*/
this.supported = [];
- // Build array of supported audio formats
- codecs.forEach(function(mimetype) {
+ // Build array of supported audio formats (if audio supported at all)
+ if (audio) {
+ codecs.forEach(function(mimetype) {
- var audio = new Audio();
- var support_level = audio.canPlayType(mimetype);
+ var support_level = audio.canPlayType(mimetype);
- // Trim semicolon and trailer
- var semicolon = mimetype.indexOf(";");
- if (semicolon != -1)
- mimetype = mimetype.substring(0, semicolon);
+ // Trim semicolon and trailer
+ var semicolon = mimetype.indexOf(";");
+ if (semicolon !== -1)
+ mimetype = mimetype.substring(0, semicolon);
- // Partition by probably/maybe
- if (support_level == "probably")
- probably_supported.push(mimetype);
- else if (support_level == "maybe")
- maybe_supported.push(mimetype);
+ // Partition by probably/maybe
+ if (support_level === "probably")
+ probably_supported.push(mimetype);
+ else if (support_level === "maybe")
+ maybe_supported.push(mimetype);
- });
+ });
- // Add probably supported types first
- Array.prototype.push.apply(
- this.supported, probably_supported);
+ // Add probably supported types first
+ Array.prototype.push.apply(
+ this.supported, probably_supported);
- // Prioritize "maybe" supported types second
- Array.prototype.push.apply(
- this.supported, maybe_supported);
+ // Prioritize "maybe" supported types second
+ Array.prototype.push.apply(
+ this.supported, maybe_supported);
+ }
})();
diff --git a/guacamole/src/main/webapp/app/client/styles/menu.css b/guacamole/src/main/webapp/app/client/styles/menu.css
index dd96408cc..f3f324025 100644
--- a/guacamole/src/main/webapp/app/client/styles/menu.css
+++ b/guacamole/src/main/webapp/app/client/styles/menu.css
@@ -21,10 +21,10 @@
*/
#menu {
- overflow: auto;
+ overflow: hidden;
position: absolute;
top: 0;
- bottom: 0;
+ height: 100%;
max-width: 100%;
width: 480px;
background: #EEE;
@@ -37,6 +37,43 @@
transition: left 0.125s, opacity 0.125s;
}
+.menu-content {
+ overflow: hidden;
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+ height: 100%;
+}
+
+.menu-header {
+ display: table-row;
+ height: 0;
+}
+
+.menu-header h2 {
+ margin-bottom: 0;
+}
+
+.menu-body {
+ display: table-row;
+ height: 100%;
+}
+
+.menu-body-content {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.menu-body-scroll-region {
+ overflow: auto;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
#menu h3 {
margin: 1em;
}
diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html
index 55781ec8a..685045312 100644
--- a/guacamole/src/main/webapp/app/client/templates/client.html
+++ b/guacamole/src/main/webapp/app/client/templates/client.html
@@ -20,8 +20,9 @@
THE SOFTWARE.
-->
-