diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
index 9fe76a4b1..d35facdfe 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
@@ -117,6 +117,12 @@ public class Field {
*/
public static String QUERY_PARAMETER = "QUERY_PARAMETER";
+ /**
+ * A color scheme accepted by the Guacamole server terminal emulator
+ * and protocols which leverage it.
+ */
+ public static String TERMINAL_COLOR_SCHEME = "TERMINAL_COLOR_SCHEME";
+
}
/**
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/TerminalColorSchemeField.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/TerminalColorSchemeField.java
new file mode 100644
index 000000000..ccd7ce53c
--- /dev/null
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/TerminalColorSchemeField.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.form;
+
+/**
+ * Represents a terminal color scheme field. The field may contain only valid
+ * terminal color schemes as used by the Guacamole server terminal emulator
+ * and protocols which leverage it (SSH, telnet, Kubernetes).
+ */
+public class TerminalColorSchemeField extends Field {
+
+ /**
+ * Creates a new TerminalColorSchemeField with the given name.
+ *
+ * @param name
+ * The unique name to associate with this field.
+ */
+ public TerminalColorSchemeField(String name) {
+ super(name, Field.Type.TERMINAL_COLOR_SCHEME);
+ }
+
+}
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/kubernetes.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/kubernetes.json
index 94ec7381c..17df48a30 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/kubernetes.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/kubernetes.json
@@ -68,7 +68,7 @@
"fields" : [
{
"name" : "color-scheme",
- "type" : "TEXT",
+ "type" : "TERMINAL_COLOR_SCHEME",
"options" : [ "", "black-white", "gray-black", "green-black", "white-black" ]
},
{
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
index 25537daef..a1d01edab 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
@@ -47,7 +47,7 @@
"fields" : [
{
"name" : "color-scheme",
- "type" : "TEXT",
+ "type" : "TERMINAL_COLOR_SCHEME",
"options" : [ "", "black-white", "gray-black", "green-black", "white-black" ]
},
{
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
index 02f9a20fb..0f70f0503 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
@@ -51,7 +51,7 @@
"fields" : [
{
"name" : "color-scheme",
- "type" : "TEXT",
+ "type" : "TERMINAL_COLOR_SCHEME",
"options" : [ "", "black-white", "gray-black", "green-black", "white-black" ]
},
{
diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index 5552e4b64..d7cf465f2 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -508,6 +508,13 @@
1.0.10
+
+
+ org.webjars.npm
+ simonwep__pickr
+ 1.2.6
+
+
diff --git a/guacamole/src/licenses/LICENSE b/guacamole/src/licenses/LICENSE
index c3d308d86..29958110b 100644
--- a/guacamole/src/licenses/LICENSE
+++ b/guacamole/src/licenses/LICENSE
@@ -697,6 +697,37 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+Pickr (https://simonwep.github.io/pickr/)
+-----------------------------------------
+
+ Version: 1.2.6
+ From: 'Simon Reinisch' (https://github.com/Simonwep/)
+ License(s):
+ MIT (bundled/pickr-1.2.6/LICENSE)
+
+MIT License
+
+Copyright (c) 2019 Simon Reinisch
+
+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.
+
+
Simple Logging Facade for Java (http://slf4j.org/)
--------------------------------------------------
diff --git a/guacamole/src/licenses/bundled/pickr-1.2.6/LICENSE b/guacamole/src/licenses/bundled/pickr-1.2.6/LICENSE
new file mode 100644
index 000000000..e02b384f9
--- /dev/null
+++ b/guacamole/src/licenses/bundled/pickr-1.2.6/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Simon Reinisch
+
+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.
diff --git a/guacamole/src/main/webapp/app/form/controllers/terminalColorSchemeFieldController.js b/guacamole/src/main/webapp/app/form/controllers/terminalColorSchemeFieldController.js
new file mode 100644
index 000000000..fb85a501d
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/controllers/terminalColorSchemeFieldController.js
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * Controller for terminal color scheme fields.
+ */
+angular.module('form').controller('terminalColorSchemeFieldController', ['$scope', '$injector',
+ function terminalColorSchemeFieldController($scope, $injector) {
+
+ // Required types
+ var ColorScheme = $injector.get('ColorScheme');
+
+ /**
+ * The currently selected color scheme. If a pre-defined color scheme is
+ * selected, this will be the connection parameter value associated with
+ * that color scheme. If a custom color scheme is selected, this will be
+ * the string "custom".
+ *
+ * @type String
+ */
+ $scope.selectedColorScheme = '';
+
+ /**
+ * The current custom color scheme, if a custom color scheme has been
+ * specified. If no custom color scheme has yet been specified, this will
+ * be a ColorScheme instance that has been initialized to the default
+ * colors.
+ *
+ * @type ColorScheme
+ */
+ $scope.customColorScheme = new ColorScheme();
+
+ /**
+ * The array of colors to include within the color picker as pre-defined
+ * options for convenience.
+ *
+ * @type String[]
+ */
+ $scope.defaultPalette = new ColorScheme().colors;
+
+ /**
+ * Whether the raw details of the custom color scheme should be shown. By
+ * default, such details are hidden.
+ *
+ * @type Boolean
+ */
+ $scope.detailsShown = false;
+
+ /**
+ * The palette indices of all colors which are considered low-intensity.
+ *
+ * @type Number[]
+ */
+ $scope.lowIntensity = [ 0, 1, 2, 3, 4, 5, 6, 7 ];
+
+ /**
+ * The palette indices of all colors which are considered high-intensity.
+ *
+ * @type Number[]
+ */
+ $scope.highIntensity = [ 8, 9, 10, 11, 12, 13, 14, 15 ];
+
+ /**
+ * The string value which is assigned to selectedColorScheme if a custom
+ * color scheme is selected.
+ *
+ * @constant
+ * @type String
+ */
+ var CUSTOM_COLOR_SCHEME = 'custom';
+
+ /**
+ * Returns whether a custom color scheme has been selected.
+ *
+ * @returns {Boolean}
+ * true if a custom color scheme has been selected, false otherwise.
+ */
+ $scope.isCustom = function isCustom() {
+ return $scope.selectedColorScheme === CUSTOM_COLOR_SCHEME;
+ };
+
+ /**
+ * Shows the raw details of the custom color scheme. If the details are
+ * already shown, this function has no effect.
+ */
+ $scope.showDetails = function showDetails() {
+ $scope.detailsShown = true;
+ };
+
+ /**
+ * Hides the raw details of the custom color scheme. If the details are
+ * already hidden, this function has no effect.
+ */
+ $scope.hideDetails = function hideDetails() {
+ $scope.detailsShown = false;
+ };
+
+ // Keep selected color scheme and custom color scheme in sync with changes
+ // to model
+ $scope.$watch('model', function modelChanged(model) {
+ if ($scope.selectedColorScheme === CUSTOM_COLOR_SCHEME || (model && !_.includes($scope.field.options, model))) {
+ $scope.customColorScheme = ColorScheme.fromString(model);
+ $scope.selectedColorScheme = CUSTOM_COLOR_SCHEME;
+ }
+ else
+ $scope.selectedColorScheme = model || '';
+ });
+
+ // Keep model in sync with changes to selected color scheme
+ $scope.$watch('selectedColorScheme', function selectedColorSchemeChanged(selectedColorScheme) {
+ if (!selectedColorScheme)
+ $scope.model = '';
+ else if (selectedColorScheme === CUSTOM_COLOR_SCHEME)
+ $scope.model = ColorScheme.toString($scope.customColorScheme);
+ else
+ $scope.model = selectedColorScheme;
+ });
+
+ // Keep model in sync with changes to custom color scheme
+ $scope.$watch('customColorScheme', function customColorSchemeChanged(customColorScheme) {
+ if ($scope.selectedColorScheme === CUSTOM_COLOR_SCHEME)
+ $scope.model = ColorScheme.toString(customColorScheme);
+ }, true);
+
+}]);
diff --git a/guacamole/src/main/webapp/app/form/controllers/textFieldController.js b/guacamole/src/main/webapp/app/form/controllers/textFieldController.js
index b5bc7538f..8dd134ab5 100644
--- a/guacamole/src/main/webapp/app/form/controllers/textFieldController.js
+++ b/guacamole/src/main/webapp/app/form/controllers/textFieldController.js
@@ -35,6 +35,6 @@ angular.module('form').controller('textFieldController', ['$scope', '$injector',
// Generate unique ID for datalist, if applicable
if ($scope.field.options && $scope.field.options.length)
- $scope.dataListId = $scope.field.name + '-datalist';
+ $scope.dataListId = $scope.fieldId + '-datalist';
}]);
diff --git a/guacamole/src/main/webapp/app/form/directives/formField.js b/guacamole/src/main/webapp/app/form/directives/formField.js
index cf3efd027..fbc0cfecf 100644
--- a/guacamole/src/main/webapp/app/form/directives/formField.js
+++ b/guacamole/src/main/webapp/app/form/directives/formField.js
@@ -87,6 +87,18 @@ angular.module('form').directive('guacFormField', [function formField() {
*/
var fieldContent = $element.find('.form-field');
+ /**
+ * An ID value which is reasonably likely to be unique relative to
+ * other elements on the page. This ID should be used to associate
+ * the relevant input element with the label provided by the
+ * guacFormField directive, if there is such an input element.
+ *
+ * @type String
+ */
+ $scope.fieldId = 'guac-field-XXXXXXXXXXXXXXXX'.replace(/X/g, function getRandomCharacter() {
+ return Math.floor(Math.random() * 36).toString(36);
+ }) + '-' + new Date().getTime().toString(36);
+
/**
* Produces the translation string for the header of the current
* field. The translation string will be of the form:
@@ -173,4 +185,4 @@ angular.module('form').directive('guacFormField', [function formField() {
}] // end controller
};
-}]);
\ No newline at end of file
+}]);
diff --git a/guacamole/src/main/webapp/app/form/directives/guacInputColor.js b/guacamole/src/main/webapp/app/form/directives/guacInputColor.js
new file mode 100644
index 000000000..3b010cc7d
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/directives/guacInputColor.js
@@ -0,0 +1,202 @@
+/*
+ * 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 directive which implements a color input field, leveraging the "Pickr"
+ * color picker. If the "Picker" color picker cannot be used because it relies
+ * on JavaScript features not supported by the browser (Internet Explorer), a
+ * "guacInputColorUnavailable" event will be emitted up the scope, and this
+ * directive will become read-only, functioning essentially as a color preview.
+ */
+angular.module('form').directive('guacInputColor', [function guacInputColor() {
+
+ var config = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'app/form/templates/guacInputColor.html',
+ transclude: true
+ };
+
+ config.scope = {
+
+ /**
+ * The current selected color value, in standard 6-digit hexadecimal
+ * RGB notation. When the user selects a different color using this
+ * directive, this value will updated accordingly.
+ *
+ * @type String
+ */
+ model: '=',
+
+ /**
+ * An optional array of colors to include within the color picker as a
+ * convenient selection of pre-defined colors. The colors within the
+ * array must be in standard 6-digit hexadecimal RGB notation.
+ *
+ * @type String[]
+ */
+ palette: '='
+
+ };
+
+ config.controller = ['$scope', '$element', '$injector',
+ function guacInputColorController($scope, $element, $injector) {
+
+ // Required services
+ var $q = $injector.get('$q');
+ var $translate = $injector.get('$translate');
+
+ /**
+ * Whether the color picker ("Pickr") cannot be used. In general, all
+ * browsers should support Pickr with the exception of Internet
+ * Explorer.
+ *
+ * @type Boolean
+ */
+ $scope.colorPickerUnavailable = false;
+
+ /**
+ * Returns whether the color currently selected is "dark" in the sense
+ * that the color white will have higher contrast against it than the
+ * color black.
+ *
+ * @returns {Boolean}
+ * true if the currently selected color is relatively dark (white
+ * text would provide better contrast than black), false otherwise.
+ */
+ $scope.isDark = function isDark() {
+
+ // Assume not dark if color is invalid or undefined
+ var rgb = $scope.model && /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec($scope.model);
+ if (!rgb)
+ return false;
+
+ // Parse color component values as hexadecimal
+ var red = parseInt(rgb[1], 16);
+ var green = parseInt(rgb[2], 16);
+ var blue = parseInt(rgb[3], 16);
+
+ // Convert RGB to luminance in HSL space (as defined by the
+ // relative luminance formula given by the W3C for accessibility)
+ var luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
+
+ // Consider the background to be dark if white text over that
+ // background would provide better contrast than black
+ return luminance <= 153; // 153 is the component value 0.6 converted from 0-1 to the 0-255 range
+
+ };
+
+ // Init color picker after required translation strings are available
+ $q.all({
+ 'save' : $translate('APP.ACTION_SAVE'),
+ 'cancel' : $translate('APP.ACTION_CANCEL')
+ }).then(function stringsRetrieved(strings) {
+
+ try {
+
+ /**
+ * An instance of the "Pickr" color picker, bound to the underlying
+ * element of this directive.
+ *
+ * @type Pickr
+ */
+ var pickr = Pickr.create({
+
+ // Bind color picker to the underlying element of this directive
+ el : $element[0],
+
+ // Wrap color picker dialog in Guacamole-specific class for
+ // sake of additional styling
+ appClass : 'guac-input-color-picker',
+
+ // Display color details as hex
+ defaultRepresentation : 'HEX',
+
+ // Use "monolith" theme, as a nice balance between "nano" (does
+ // not work in Internet Explorer) and "classic" (too big)
+ theme : 'monolith',
+
+ // Leverage the container element as the button which shows the
+ // picker, relying on our own styling for that button
+ useAsButton : true,
+ appendToBody : true,
+
+ // Do not include opacity controls
+ lockOpacity : true,
+
+ // Include a selection of palette entries for convenience and
+ // reference
+ swatches : $scope.palette || [],
+
+ components: {
+
+ // Include hue and color preview controls
+ preview : true,
+ hue : true,
+
+ // Display only a text color input field and the save and
+ // cancel buttons (no clear button)
+ interaction: {
+ input : true,
+ save : true,
+ cancel : true
+ }
+
+ },
+
+ // Use translation strings for buttons
+ strings : strings
+
+ });
+
+ // Hide color picker after user clicks "cancel"
+ pickr.on('cancel', function colorChangeCanceled() {
+ pickr.hide();
+ });
+
+ // Keep model in sync with changes to the color picker
+ pickr.on('save', function colorChanged(color) {
+ $scope.$evalAsync(function updateModel() {
+ $scope.model = color.toHEXA().toString();
+ });
+ });
+
+ // Keep color picker in sync with changes to the model
+ pickr.on('init', function pickrReady(color) {
+ $scope.$watch('model', function modelChanged(model) {
+ pickr.setColor(model);
+ });
+ });
+
+ }
+
+ // If the "Pickr" color picker cannot be loaded (Internet Explorer),
+ // let the scope above us know
+ catch (e) {
+ $scope.colorPickerUnavailable = true;
+ $scope.$emit('guacInputColorUnavailable', e);
+ }
+
+ }, angular.noop);
+
+ }];
+
+ return config;
+
+}]);
diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js
index 7e9c1cb6e..24c29ac85 100644
--- a/guacamole/src/main/webapp/app/form/services/formService.js
+++ b/guacamole/src/main/webapp/app/form/services/formService.js
@@ -179,6 +179,19 @@ angular.module('form').provider('formService', function formServiceProvider() {
module : 'form',
controller : 'timeFieldController',
templateUrl : 'app/form/templates/timeField.html'
+ },
+
+ /**
+ * Field type which allows selection of color schemes accepted by the
+ * Guacamole server terminal emulator and protocols which leverage it.
+ *
+ * @see {@link Field.Type.TERMINAL_COLOR_SCHEME}
+ * @type FieldType
+ */
+ 'TERMINAL_COLOR_SCHEME' : {
+ module : 'form',
+ controller : 'terminalColorSchemeFieldController',
+ templateUrl : 'app/form/templates/terminalColorSchemeField.html'
}
};
@@ -221,6 +234,11 @@ angular.module('form').provider('formService', function formServiceProvider() {
* A String which defines the unique namespace associated the
* translation strings used by the form using a field of this type.
*
+ * fieldId:
+ * A String value which is reasonably likely to be unique and may
+ * be used to associate the main element of the field with its
+ * label.
+ *
* field:
* The Field object that is being rendered, representing a field of
* this type.
diff --git a/guacamole/src/main/webapp/app/form/styles/terminal-color-scheme-field.css b/guacamole/src/main/webapp/app/form/styles/terminal-color-scheme-field.css
new file mode 100644
index 000000000..01eac1ac7
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/styles/terminal-color-scheme-field.css
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+.terminal-color-scheme-field {
+ max-width: 320px;
+}
+
+.terminal-color-scheme-field select {
+ width: 100%;
+}
+
+.terminal-color-scheme-field .custom-color-scheme {
+ background: #EEE;
+ padding: 0.5em;
+ border: 1px solid silver;
+ border-spacing: 0;
+ margin-top: -2px;
+ width: 100%;
+}
+
+.terminal-color-scheme-field .custom-color-scheme-section {
+ display: -ms-flexbox;
+ display: -moz-box;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+}
+
+.terminal-color-scheme-field .guac-input-color {
+
+ display: block;
+ margin: 2px;
+ width: 1.5em;
+ height: 1.5em;
+ min-width: 1.25em;
+ border-radius: 0.15em;
+ line-height: 1.5em;
+ text-align: center;
+ font-size: 0.75em;
+ cursor: pointer;
+ color: black;
+
+ -ms-flex: 1;
+ -moz-box-flex: 1;
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+
+}
+
+.terminal-color-scheme-field .guac-input-color.read-only {
+ cursor: not-allowed;
+}
+
+.terminal-color-scheme-field .guac-input-color.dark {
+ color: white;
+}
+
+.terminal-color-scheme-field .palette .guac-input-color {
+ font-weight: bold;
+}
+
+/* Hide palette numbers unless color scheme details are visible */
+.terminal-color-scheme-field.custom-color-scheme-details-hidden .custom-color-scheme .palette .guac-input-color {
+ color: transparent;
+}
+
+/*
+ * Custom color scheme details header
+ */
+
+.terminal-color-scheme-field .custom-color-scheme-details-header {
+ font-size: 0.8em;
+ margin: 0.5em 0;
+ padding: 0;
+}
+
+.terminal-color-scheme-field .custom-color-scheme-details-header::before {
+ content: '▸ ';
+}
+
+.terminal-color-scheme-field.custom-color-scheme-details-visible .custom-color-scheme-details-header::before {
+ content: '▾ ';
+}
+
+/*
+ * Details show/hide link
+ */
+
+/* Render show/hide as a link */
+.terminal-color-scheme-field .custom-color-scheme-hide-details,
+.terminal-color-scheme-field .custom-color-scheme-show-details {
+ color: blue;
+ text-decoration: underline;
+ cursor: pointer;
+ margin: 0 0.25em;
+ font-weight: normal;
+}
+
+.terminal-color-scheme-field .custom-color-scheme-hide-details {
+ display: none;
+}
+
+.terminal-color-scheme-field.custom-color-scheme-details-visible .custom-color-scheme-hide-details {
+ display: inline;
+}
+
+.terminal-color-scheme-field.custom-color-scheme-details-visible .custom-color-scheme-show-details {
+ display: none;
+}
+
+/*
+ * Color scheme details
+ */
+
+.terminal-color-scheme-field .custom-color-scheme-details {
+ display: none;
+}
+
+.terminal-color-scheme-field.custom-color-scheme-details-visible .custom-color-scheme-details {
+ display: block;
+ width: 100%;
+ margin: 0.5em 0;
+}
+
+/*
+ * Color picker
+ */
+
+/* Increase width of color picker to allow two even rows of eight color
+ * swatches */
+.guac-input-color-picker[data-theme="monolith"] {
+ width: 16.25em;
+}
+
+/* Remove Guacamole-specific styles inherited from the generic button rules */
+.guac-input-color-picker[data-theme="monolith"] button {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ box-shadow: none;
+}
diff --git a/guacamole/src/main/webapp/app/form/templates/checkboxField.html b/guacamole/src/main/webapp/app/form/templates/checkboxField.html
index 252441b1f..e906f7d38 100644
--- a/guacamole/src/main/webapp/app/form/templates/checkboxField.html
+++ b/guacamole/src/main/webapp/app/form/templates/checkboxField.html
@@ -1 +1,7 @@
-
+
diff --git a/guacamole/src/main/webapp/app/form/templates/dateField.html b/guacamole/src/main/webapp/app/form/templates/dateField.html
index 6fd38dab8..7673e366d 100644
--- a/guacamole/src/main/webapp/app/form/templates/dateField.html
+++ b/guacamole/src/main/webapp/app/form/templates/dateField.html
@@ -1,6 +1,7 @@
\ No newline at end of file
+
diff --git a/guacamole/src/main/webapp/app/form/templates/formField.html b/guacamole/src/main/webapp/app/form/templates/formField.html
index 45cf6b912..3e45d4cb6 100644
--- a/guacamole/src/main/webapp/app/form/templates/formField.html
+++ b/guacamole/src/main/webapp/app/form/templates/formField.html
@@ -1,9 +1,11 @@
-