GUACAMOLE-234: Merge migration from JLDAP to Apache Directory API.

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 9fe76a4..d35facd 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 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 0000000..ccd7ce5
--- /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 94ec738..17df48a 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 8eef774..6a91279 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 9b39168..2526096 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 7a68606..8f90f27 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -501,6 +501,13 @@
             <version>1.0.10</version>
         </dependency>
 
+        <!-- Pickr (JavaScript color picker) -->
+        <dependency>
+            <groupId>org.webjars.npm</groupId>
+            <artifactId>simonwep__pickr</artifactId>
+            <version>1.2.6</version>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/guacamole/src/licenses/LICENSE b/guacamole/src/licenses/LICENSE
index 1e228d2..7c696e5 100644
--- a/guacamole/src/licenses/LICENSE
+++ b/guacamole/src/licenses/LICENSE
@@ -688,6 +688,37 @@
 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 0000000..e02b384
--- /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/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index 41c6ba6..b90f263 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -27,6 +27,7 @@
     var ManagedClient      = $injector.get('ManagedClient');
     var ManagedClientState = $injector.get('ManagedClientState');
     var ManagedFilesystem  = $injector.get('ManagedFilesystem');
+    var Protocol           = $injector.get('Protocol');
     var ScrollState        = $injector.get('ScrollState');
 
     // Required services
@@ -248,7 +249,15 @@
          *
          * @type ScrollState
          */
-        scrollState : new ScrollState()
+        scrollState : new ScrollState(),
+
+        /**
+         * The current desired values of all editable connection parameters as
+         * a set of name/value pairs, including any changes made by the user.
+         *
+         * @type {Object.<String, String>}
+         */
+        connectionParameters : {}
 
     };
 
@@ -258,6 +267,16 @@
     };
 
     /**
+     * Applies any changes to connection parameters made by the user within the
+     * Guacamole menu.
+     */
+    $scope.applyParameterChanges = function applyParameterChanges() {
+        angular.forEach($scope.menu.connectionParameters, function sendArgv(value, name) {
+            ManagedClient.setArgument($scope.client, name, value);
+        });
+    };
+
+    /**
      * The client which should be attached to the client UI.
      *
      * @type ManagedClient
@@ -429,12 +448,20 @@
 
     });
 
+    // Update client state/behavior as visibility of the Guacamole menu changes
     $scope.$watch('menu.shown', function menuVisibilityChanged(menuShown, menuShownPreviousState) {
         
-        // Send clipboard data if menu is hidden
-        if (!menuShown && menuShownPreviousState)
+        // Send clipboard and argument value data once menu is hidden
+        if (!menuShown && menuShownPreviousState) {
             $scope.$broadcast('guacClipboard', $scope.client.clipboardData);
-        
+            $scope.applyParameterChanges();
+        }
+
+        // Obtain snapshot of current editable connection parameters when menu
+        // is opened
+        else if (menuShown)
+            $scope.menu.connectionParameters = ManagedClient.getArgumentModel($scope.client);
+
         // Disable client keyboard if the menu is shown
         $scope.client.clientProperties.keyboardEnabled = !menuShown;
 
@@ -806,6 +833,11 @@
     $scope.clientMenuActions = [ DISCONNECT_MENU_ACTION ];
 
     /**
+     * @borrows Protocol.getNamespace
+     */
+    $scope.getProtocolNamespace = Protocol.getNamespace;
+
+    /**
      * The currently-visible filesystem within the filesystem menu, if the
      * filesystem menu is open. If no filesystem is currently visible, this
      * will be null.
diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html
index ad85f23..7690e75 100644
--- a/guacamole/src/main/webapp/app/client/templates/client.html
+++ b/guacamole/src/main/webapp/app/client/templates/client.html
@@ -96,6 +96,14 @@
                     </div>
                 </div>
 
+                <!-- Connection parameters which may be modified while the connection is open -->
+                <div class="menu-section connection-parameters" id="connection-settings" ng-show="client.protocol">
+                    <guac-form namespace="getProtocolNamespace(client.protocol)"
+                               content="client.forms"
+                               model="menu.connectionParameters"
+                               model-only="true"></guac-form>
+                </div>
+
                 <!-- Input method -->
                 <div class="menu-section" id="keyboard-settings">
                     <h3>{{'CLIENT.SECTION_HEADER_INPUT_METHOD' | translate}}</h3>
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedArgument.js b/guacamole/src/main/webapp/app/client/types/ManagedArgument.js
new file mode 100644
index 0000000..247d9f6
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/types/ManagedArgument.js
@@ -0,0 +1,152 @@
+/*
+ * 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 ManagedArgument class used by ManagedClient.
+ */
+angular.module('client').factory('ManagedArgument', ['$q', function defineManagedArgument($q) {
+
+    /**
+     * Object which represents an argument (connection parameter) which may be
+     * changed by the user while the connection is open.
+     * 
+     * @constructor
+     * @param {ManagedArgument|Object} [template={}]
+     *     The object whose properties should be copied within the new
+     *     ManagedArgument.
+     */
+    var ManagedArgument = function ManagedArgument(template) {
+
+        // Use empty object by default
+        template = template || {};
+
+        /**
+         * The name of the connection parameter.
+         *
+         * @type {String}
+         */
+        this.name = template.name;
+
+        /**
+         * The current value of the connection parameter.
+         *
+         * @type {String}
+         */
+        this.value = template.value;
+
+        /**
+         * A valid, open output stream which may be used to apply a new value
+         * to the connection parameter.
+         *
+         * @type {Guacamole.OutputStream}
+         */
+        this.stream = template.stream;
+
+    };
+
+    /**
+     * Requests editable access to a given connection parameter, returning a
+     * promise which is resolved with a ManagedArgument instance that provides
+     * such access if the parameter is indeed editable.
+     *
+     * @param {ManagedClient} managedClient
+     *     The ManagedClient instance associated with the connection for which
+     *     an editable version of the connection parameter is being retrieved.
+     *
+     * @param {String} name
+     *     The name of the connection parameter.
+     *
+     * @param {String} value
+     *     The current value of the connection parameter, as received from a
+     *     prior, inbound "argv" stream.
+     *
+     * @returns {Promise.<ManagedArgument>}
+     *     A promise which is resolved with the new ManagedArgument instance
+     *     once the requested parameter has been verified as editable.
+     */
+    ManagedArgument.getInstance = function getInstance(managedClient, name, value) {
+
+        var deferred = $q.defer();
+
+        // Create internal, fully-populated instance of ManagedArgument, to be
+        // returned only once mutability of the associated connection parameter
+        // has been verified
+        var managedArgument = new ManagedArgument({
+            name   : name,
+            value  : value,
+            stream : managedClient.client.createArgumentValueStream('text/plain', name)
+        });
+
+        // The connection parameter is editable only if a successful "ack" is
+        // received
+        managedArgument.stream.onack = function ackReceived(status) {
+            if (status.isError())
+                deferred.reject(status);
+            else
+                deferred.resolve(managedArgument);
+        };
+
+        return deferred.promise;
+
+    };
+
+    /**
+     * Sets the given editable argument (connection parameter) to the given
+     * value, updating the behavior of the associated connection in real-time.
+     * If successful, the ManagedArgument provided cannot be used for future
+     * calls to setValue() and must be replaced with a new instance. This
+     * function only has an effect if the new parameter value is different from
+     * the current value.
+     *
+     * @param {ManagedArgument} managedArgument
+     *     The ManagedArgument instance associated with the connection
+     *     parameter being modified.
+     *
+     * @param {String} value
+     *     The new value to assign to the connection parameter.
+     *
+     * @returns {Boolean}
+     *     true if the connection parameter was sent and the provided
+     *     ManagedArgument instance may no longer be used for future setValue()
+     *     calls, false if the connection parameter was NOT sent as it has not
+     *     changed.
+     */
+    ManagedArgument.setValue = function setValue(managedArgument, value) {
+
+        // Stream new value only if value has changed
+        if (value !== managedArgument.value) {
+
+            var writer = new Guacamole.StringWriter(managedArgument.stream);
+            writer.sendText(value);
+            writer.sendEnd();
+
+            // ManagedArgument instance is no longer usable
+            return true;
+
+        }
+
+        // No parameter value change was attempted and the ManagedArgument
+        // instance may be reused
+        return false;
+
+    };
+
+    return ManagedArgument;
+
+}]);
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index b4637e9..4551208 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -27,6 +27,7 @@
     var ClientProperties       = $injector.get('ClientProperties');
     var ClientIdentifier       = $injector.get('ClientIdentifier');
     var ClipboardData          = $injector.get('ClipboardData');
+    var ManagedArgument        = $injector.get('ManagedArgument');
     var ManagedClientState     = $injector.get('ManagedClientState');
     var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
     var ManagedDisplay         = $injector.get('ManagedDisplay');
@@ -44,6 +45,7 @@
     var connectionService      = $injector.get('connectionService');
     var preferenceService      = $injector.get('preferenceService');
     var requestService         = $injector.get('requestService');
+    var schemaService          = $injector.get('schemaService');
     var tunnelService          = $injector.get('tunnelService');
     var guacAudio              = $injector.get('guacAudio');
     var guacHistory            = $injector.get('guacHistory');
@@ -118,6 +120,23 @@
         this.title = template.title;
 
         /**
+         * The name which uniquely identifies the protocol of the connection in
+         * use. If the protocol cannot be determined, such as when a connection
+         * group is in use, this will be null.
+         *
+         * @type {String}
+         */
+        this.protocol = template.protocol || null;
+
+        /**
+         * An array of forms describing all known parameters for the connection
+         * in use, including those which may not be editable.
+         *
+         * @type {Form[]}
+         */
+        this.forms = template.forms || [];
+
+        /**
          * The most recently-generated thumbnail for this connection, as
          * stored within the local connection history. If no thumbnail is
          * stored, this will be null.
@@ -179,6 +198,17 @@
          */
         this.clientProperties = template.clientProperties || new ClientProperties();
 
+        /**
+         * All editable arguments (connection parameters), stored by their
+         * names. Arguments will only be present within this set if their
+         * current values have been exposed by the server via an inbound "argv"
+         * stream and the server has confirmed that the value may be changed
+         * through a successful "ack" to an outbound "argv" stream.
+         *
+         * @type {Object.<String, ManagedArgument>}
+         */
+        this.arguments = template.arguments || {};
+
     };
 
     /**
@@ -448,6 +478,33 @@
 
         };
 
+        // Test for argument mutability whenever an argument value is
+        // received
+        client.onargv = function clientArgumentValueReceived(stream, mimetype, name) {
+
+            // Ignore arguments which do not use a mimetype currently supported
+            // by the web application
+            if (mimetype !== 'text/plain')
+                return;
+
+            var reader = new Guacamole.StringReader(stream);
+
+            // Assemble received data into a single string
+            var value = '';
+            reader.ontext = function textReceived(text) {
+                value += text;
+            };
+
+            // Test mutability once stream is finished, storing the current
+            // value for the argument only if it is mutable
+            reader.onend = function textComplete() {
+                ManagedArgument.getInstance(managedClient, name, value).then(function argumentIsMutable(argument) {
+                    managedClient.arguments[name] = argument;
+                }, function ignoreImmutableArguments() {});
+            };
+
+        };
+
         // Handle any received clipboard data
         client.onclipboard = function clientClipboardReceived(stream, mimetype) {
 
@@ -522,11 +579,16 @@
             client.connect(connectString);
         });
 
-        // If using a connection, pull connection name
+        // If using a connection, pull connection name and protocol information
         if (clientIdentifier.type === ClientIdentifier.Types.CONNECTION) {
-            connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id)
-            .then(function connectionRetrieved(connection) {
-                managedClient.name = managedClient.title = connection.name;
+            $q.all({
+                connection : connectionService.getConnection(clientIdentifier.dataSource, clientIdentifier.id),
+                protocols  : schemaService.getProtocols(clientIdentifier.dataSource)
+            })
+            .then(function dataRetrieved(values) {
+                managedClient.name = managedClient.title = values.connection.name;
+                managedClient.protocol = values.connection.protocol;
+                managedClient.forms = values.protocols[values.connection.protocol].connectionForms;
             }, requestService.WARN);
         }
         
@@ -620,6 +682,52 @@
     };
 
     /**
+     * Assigns the given value to the connection parameter having the given
+     * name, updating the behavior of the connection in real-time. If the
+     * connection parameter is not editable, this function has no effect.
+     *
+     * @param {ManagedClient} managedClient
+     *     The ManagedClient instance associated with the active connection
+     *     being modified.
+     *
+     * @param {String} name
+     *     The name of the connection parameter to modify.
+     *
+     * @param {String} value
+     *     The value to attempt to assign to the given connection parameter.
+     */
+    ManagedClient.setArgument = function setArgument(managedClient, name, value) {
+        var managedArgument = managedClient.arguments[name];
+        if (managedArgument && ManagedArgument.setValue(managedArgument, value))
+            delete managedClient.arguments[name];
+    };
+
+    /**
+     * Retrieves the current values of all editable connection parameters as a
+     * set of name/value pairs suitable for use as the model of a form which
+     * edits those parameters.
+     *
+     * @param {ManagedClient} client
+     *     The ManagedClient instance associated with the active connection
+     *     whose parameter values are being retrieved.
+     *
+     * @returns {Object.<String, String>}
+     *     A new set of name/value pairs containing the current values of all
+     *     editable parameters.
+     */
+    ManagedClient.getArgumentModel = function getArgumentModel(client) {
+
+        var model = {};
+
+        angular.forEach(client.arguments, function addModelEntry(managedArgument) {
+            model[managedArgument.name] = managedArgument.value;
+        });
+
+        return model;
+
+    };
+
+    /**
      * Produces a sharing link for the given ManagedClient using the given
      * sharing profile. The resulting sharing link, and any required login
      * information, can be retrieved from the <code>shareLinks</code> property
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 0000000..fb85a50
--- /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 b5bc753..8dd134a 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 @@
 
     // 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 ea0f35f..41db0c1 100644
--- a/guacamole/src/main/webapp/app/form/directives/formField.js
+++ b/guacamole/src/main/webapp/app/form/directives/formField.js
@@ -73,6 +73,18 @@
             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:
              *
@@ -158,4 +170,4 @@
         }] // 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 0000000..3b010cc
--- /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 6019e74..cc0a240 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 @@
             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 @@
          *     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 0000000..01eac1a
--- /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 ad9d8e0..110ab14 100644
--- a/guacamole/src/main/webapp/app/form/templates/checkboxField.html
+++ b/guacamole/src/main/webapp/app/form/templates/checkboxField.html
@@ -1 +1,5 @@
-<input type="checkbox" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
\ No newline at end of file
+<input type="checkbox"
+       ng-attr-id="{{ fieldId }}"
+       ng-model="typedValue"
+       autocorrect="off"
+       autocapitalize="off"/>
diff --git a/guacamole/src/main/webapp/app/form/templates/dateField.html b/guacamole/src/main/webapp/app/form/templates/dateField.html
index a186e19..2da96a1 100644
--- a/guacamole/src/main/webapp/app/form/templates/dateField.html
+++ b/guacamole/src/main/webapp/app/form/templates/dateField.html
@@ -1,5 +1,6 @@
 <div class="date-field">
     <input type="date"
+           ng-attr-id="{{ fieldId }}"
            ng-model="typedValue"
            ng-model-options="modelOptions"
            guac-lenient-date
diff --git a/guacamole/src/main/webapp/app/form/templates/emailField.html b/guacamole/src/main/webapp/app/form/templates/emailField.html
index db6d3be..4ef14f4 100644
--- a/guacamole/src/main/webapp/app/form/templates/emailField.html
+++ b/guacamole/src/main/webapp/app/form/templates/emailField.html
@@ -1,8 +1,9 @@
 <div class="email-field">
     <input type="email"
+           ng-attr-id="{{ fieldId }}"
            ng-model="model"
            ng-hide="readOnly"
            autocorrect="off"
            autocapitalize="off"/>
     <a href="mailto:{{model}}" ng-show="readOnly">{{model}}</a>
-</div>
\ No newline at end of file
+</div>
diff --git a/guacamole/src/main/webapp/app/form/templates/formField.html b/guacamole/src/main/webapp/app/form/templates/formField.html
index 45cf6b9..3e45d4c 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 @@
-<label class="labeled-field" ng-class="{empty: !model}" ng-show="isFieldVisible()">
+<div class="labeled-field" ng-class="{empty: !model}" ng-show="isFieldVisible()">
 
     <!-- Field header -->
-    <span class="field-header">{{getFieldHeader() | translate}}</span>
+    <div class="field-header">
+        <label ng-attr-for="{{ fieldId }}">{{getFieldHeader() | translate}}</label>
+    </div>
 
     <!-- Field content -->
     <div class="form-field"></div>
 
-</label>
+</div>
diff --git a/guacamole/src/main/webapp/app/form/templates/guacInputColor.html b/guacamole/src/main/webapp/app/form/templates/guacInputColor.html
new file mode 100644
index 0000000..fc6e675
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/templates/guacInputColor.html
@@ -0,0 +1,10 @@
+<div class="guac-input-color"
+     ng-class="{
+         'dark' : isDark(),
+         'read-only' : colorPickerUnavailable
+     }"
+     ng-style="{
+        'background-color' : model
+     }">
+    <ng-transclude></ng-transclude>
+</div>
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/form/templates/languageField.html b/guacamole/src/main/webapp/app/form/templates/languageField.html
index 404f74e..b1f25e8 100644
--- a/guacamole/src/main/webapp/app/form/templates/languageField.html
+++ b/guacamole/src/main/webapp/app/form/templates/languageField.html
@@ -1 +1,3 @@
-<select ng-model="model" ng-options="language.key as language.value for language in languages | toArray | orderBy: key"></select>
\ No newline at end of file
+<select ng-attr-id="{{ fieldId }}"
+        ng-model="model"
+        ng-options="language.key as language.value for language in languages | toArray | orderBy: key"></select>
diff --git a/guacamole/src/main/webapp/app/form/templates/numberField.html b/guacamole/src/main/webapp/app/form/templates/numberField.html
index 3d6312e..f802b4a 100644
--- a/guacamole/src/main/webapp/app/form/templates/numberField.html
+++ b/guacamole/src/main/webapp/app/form/templates/numberField.html
@@ -1 +1,5 @@
-<input type="number" ng-model="typedValue" autocorrect="off" autocapitalize="off"/>
\ No newline at end of file
+<input type="number"
+       ng-attr-id="{{ fieldId }}"
+       ng-model="typedValue"
+       autocorrect="off"
+       autocapitalize="off"/>
diff --git a/guacamole/src/main/webapp/app/form/templates/passwordField.html b/guacamole/src/main/webapp/app/form/templates/passwordField.html
index 506d8b6..69d67e8 100644
--- a/guacamole/src/main/webapp/app/form/templates/passwordField.html
+++ b/guacamole/src/main/webapp/app/form/templates/passwordField.html
@@ -1,4 +1,9 @@
 <div class="password-field">
-    <input type="{{passwordInputType}}" ng-model="model" ng-trim="false" autocorrect="off" autocapitalize="off"/>
+    <input type="{{passwordInputType}}"
+           ng-attr-id="{{ fieldId }}"
+           ng-model="model"
+           ng-trim="false"
+           autocorrect="off"
+           autocapitalize="off"/>
     <div class="icon toggle-password" ng-click="togglePassword()" title="{{getTogglePasswordHelpText() | translate}}"></div>
-</div>
\ No newline at end of file
+</div>
diff --git a/guacamole/src/main/webapp/app/form/templates/selectField.html b/guacamole/src/main/webapp/app/form/templates/selectField.html
index 3bd2bb8..e5ce0e9 100644
--- a/guacamole/src/main/webapp/app/form/templates/selectField.html
+++ b/guacamole/src/main/webapp/app/form/templates/selectField.html
@@ -1 +1,3 @@
-<select ng-model="model" ng-options="option as getFieldOption(option) | translate for option in field.options | orderBy: value"></select>
\ No newline at end of file
+<select ng-attr-id="{{ fieldId }}"
+        ng-model="model"
+        ng-options="option as getFieldOption(option) | translate for option in field.options | orderBy: value"></select>
diff --git a/guacamole/src/main/webapp/app/form/templates/terminalColorSchemeField.html b/guacamole/src/main/webapp/app/form/templates/terminalColorSchemeField.html
new file mode 100644
index 0000000..a8425e4
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/templates/terminalColorSchemeField.html
@@ -0,0 +1,63 @@
+<div class="terminal-color-scheme-field" ng-class="{
+        'custom-color-scheme-details-visible' : detailsShown,
+        'custom-color-scheme-details-hidden' : !detailsShown
+    }">
+
+    <!-- Pre-defined color scheme options -->
+    <select ng-attr-id="{{ fieldId }}" ng-model="selectedColorScheme">
+        <option ng-repeat="option in field.options | orderBy: value"
+                ng-value="option">{{ getFieldOption(option) | translate }}</option>
+        <option value="custom">{{ 'COLOR_SCHEME.FIELD_OPTION_CUSTOM' | translate }}</option>
+    </select>
+
+    <!-- Custom color scheme -->
+    <div class="custom-color-scheme" ng-show="isCustom()">
+
+        <!-- Default foreground color -->
+        <div class="custom-color-scheme-section default-color foreground">
+            <guac-input-color model="customColorScheme.foreground"
+                              palette="defaultPalette">
+                {{ 'COLOR_SCHEME.FIELD_HEADER_FOREGROUND' | translate }}
+            </guac-input-color>
+        </div>
+
+        <!-- Default background color -->
+        <div class="custom-color-scheme-section default-color background">
+            <guac-input-color model="customColorScheme.background"
+                              palette="defaultPalette">
+                {{ 'COLOR_SCHEME.FIELD_HEADER_BACKGROUND' | translate }}
+            </guac-input-color>
+        </div>
+
+        <!-- Low intensity portion of 16-color palette -->
+        <div class="custom-color-scheme-section palette low-intensity">
+            <guac-input-color ng-repeat="index in lowIntensity"
+                              model="customColorScheme.colors[index]"
+                              palette="defaultPalette">
+                {{ index }}
+            </guac-input-color>
+        </div>
+
+        <!-- High intensity portion of 16-color palette -->
+        <div class="custom-color-scheme-section palette high-intensity">
+            <guac-input-color ng-repeat="index in highIntensity"
+                              model="customColorScheme.colors[index]"
+                              palette="defaultPalette">
+                {{ index }}
+            </guac-input-color>
+        </div>
+
+    </div>
+
+    <!-- Show/hide details -->
+    <h4 class="custom-color-scheme-details-header" ng-show="isCustom()">
+        {{'COLOR_SCHEME.SECTION_HEADER_DETAILS' | translate}}
+        <a class="custom-color-scheme-show-details" ng-click="showDetails()">{{'COLOR_SCHEME.ACTION_SHOW_DETAILS' | translate}}</a>
+        <a class="custom-color-scheme-hide-details" ng-click="hideDetails()">{{'COLOR_SCHEME.ACTION_HIDE_DETAILS' | translate}}</a>
+    </h4>
+
+    <!-- Custom color scheme details (internal representation -->
+    <textarea class="custom-color-scheme-details" spellcheck="false"
+              ng-model="model" ng-show="isCustom()"></textarea>
+
+</div>
diff --git a/guacamole/src/main/webapp/app/form/templates/textAreaField.html b/guacamole/src/main/webapp/app/form/templates/textAreaField.html
index 082476f..2d4144b 100644
--- a/guacamole/src/main/webapp/app/form/templates/textAreaField.html
+++ b/guacamole/src/main/webapp/app/form/templates/textAreaField.html
@@ -1 +1,4 @@
-<textarea ng-model="model" autocorrect="off" autocapitalize="off"></textarea>
\ No newline at end of file
+<textarea ng-attr-id="{{ fieldId }}"
+          ng-model="model"
+          autocorrect="off"
+          autocapitalize="off"></textarea>
diff --git a/guacamole/src/main/webapp/app/form/templates/textField.html b/guacamole/src/main/webapp/app/form/templates/textField.html
index a338db4..938b81c 100644
--- a/guacamole/src/main/webapp/app/form/templates/textField.html
+++ b/guacamole/src/main/webapp/app/form/templates/textField.html
@@ -1,7 +1,12 @@
 <div class="text-field">
-    <input type="text" ng-model="model" autocorrect="off" autocapitalize="off" ng-attr-list="{{ dataListId }}"/>
-    <datalist ng-if="dataListId" id="{{ dataListId }}">
+    <input type="text"
+           ng-attr-id="{{ fieldId }}"
+           ng-attr-list="{{ dataListId }}"
+           ng-model="model"
+           autocorrect="off"
+           autocapitalize="off"/>
+    <datalist ng-if="dataListId" ng-attr-id="{{ dataListId }}">
         <option ng-repeat="option in field.options | orderBy: option"
                 value="{{ option }}">{{ getFieldOption(option) | translate }}</option>
     </datalist>
-</div>
\ No newline at end of file
+</div>
diff --git a/guacamole/src/main/webapp/app/form/templates/timeField.html b/guacamole/src/main/webapp/app/form/templates/timeField.html
index 24ae968..292e44c 100644
--- a/guacamole/src/main/webapp/app/form/templates/timeField.html
+++ b/guacamole/src/main/webapp/app/form/templates/timeField.html
@@ -1,5 +1,6 @@
 <div class="time-field">
     <input type="time"
+           ng-attr-id="{{ fieldId }}"
            ng-model="typedValue"
            ng-model-options="modelOptions"
            guac-lenient-time
diff --git a/guacamole/src/main/webapp/app/form/templates/timeZoneField.html b/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
index 15fd4d6..ed960af 100644
--- a/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
+++ b/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
@@ -2,6 +2,7 @@
 
     <!-- Available time zone regions -->
     <select class="time-zone-region"
+            ng-attr-id="{{ fieldId }}"
             ng-model="region"
             ng-options="name for name in regions | orderBy: name"></select>
 
diff --git a/guacamole/src/main/webapp/app/form/types/ColorScheme.js b/guacamole/src/main/webapp/app/form/types/ColorScheme.js
new file mode 100644
index 0000000..f51a667
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/types/ColorScheme.js
@@ -0,0 +1,949 @@
+/*
+ * 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.
+ */
+
+/**
+ * Service which defines the ColorScheme class.
+ */
+angular.module('form').factory('ColorScheme', [function defineColorScheme() {
+ 
+    /**
+     * Intermediate representation of a custom color scheme which can be
+     * converted to the color scheme format used by Guacamole's terminal
+     * emulator. All colors must be represented in the six-digit hexadecimal
+     * RGB notation used by HTML ("#000000" for black, etc.).
+     * 
+     * @constructor
+     * @param {ColorScheme|Object} [template={}]
+     *     The object whose properties should be copied within the new
+     *     ColorScheme.
+     */
+    var ColorScheme = function ColorScheme(template) {
+
+        // Use empty object by default
+        template = template || {};
+
+        /**
+         * The terminal background color. This will be the default foreground
+         * color of the Guacamole terminal emulator ("#000000") by default.
+         *
+         * @type {String}
+         */
+        this.background = template.background || '#000000';
+
+        /**
+         * The terminal foreground color. This will be the default foreground
+         * color of the Guacamole terminal emulator ("#999999") by default.
+         *
+         * @type {String}
+         */
+        this.foreground = template.foreground || '#999999';
+
+        /**
+         * The terminal color palette. Default values are provided for the
+         * normal 16 terminal colors using the default values of the Guacamole
+         * terminal emulator, however the terminal emulator and this
+         * representation support up to 256 colors.
+         *
+         * @type {String[]}
+         */
+        this.colors = template.colors || [
+
+            // Normal colors
+            '#000000', // Black
+            '#993E3E', // Red
+            '#3E993E', // Green
+            '#99993E', // Brown
+            '#3E3E99', // Blue
+            '#993E99', // Magenta
+            '#3E9999', // Cyan
+            '#999999', // White
+
+            // Intense colors
+            '#3E3E3E', // Black
+            '#FF6767', // Red
+            '#67FF67', // Green
+            '#FFFF67', // Brown
+            '#6767FF', // Blue
+            '#FF67FF', // Magenta
+            '#67FFFF', // Cyan
+            '#FFFFFF'  // White
+
+        ];
+
+        /**
+         * The string which was parsed to produce this ColorScheme instance, if
+         * ColorScheme.fromString() was used to produce this ColorScheme.
+         *
+         * @private
+         * @type {String}
+         */
+        this._originalString = template._originalString;
+
+    };
+
+    /**
+     * Given a color string in the standard 6-digit hexadecimal RGB format,
+     * returns a X11 color spec which represents the same color.
+     *
+     * @param {String} color
+     *     The hexadecimal color string to convert.
+     *
+     * @returns {String}
+     *     The X11 color spec representing the same color as the given
+     *     hexadecimal string, or null if the given string is not a valid
+     *     6-digit hexadecimal RGB color.
+     */
+    var fromHexColor = function fromHexColor(color) {
+
+        var groups = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(color);
+        if (!groups)
+            return null;
+
+        return 'rgb:' + groups[1] + '/' + groups[2] + '/' + groups[3];
+
+    };
+
+    /**
+     * Parses the same subset of the X11 color spec supported by the Guacamole
+     * terminal emulator (the "rgb:*" format), returning the equivalent 6-digit
+     * hexadecimal color string supported by the ColorScheme representation.
+     * The X11 color spec defined by Xlib's XParseColor(). The human-readable
+     * color names supported by the Guacamole terminal emulator (the same color
+     * names as supported by xterm) may also be used.
+     *
+     * @param {String} color
+     *     The X11 color spec to parse, or the name of a known named color.
+     *
+     * @returns {String}
+     *     The 6-digit hexadecimal color string which represents the same color
+     *     as the given X11 color spec/name, or null if the given spec/name is
+     *     invalid.
+     */
+    var toHexColor = function toHexColor(color) {
+
+        /**
+         * Shifts or truncates the given hexadecimal string such that it
+         * contains exactly two hexadecimal digits, as required by any
+         * individual color component of the 6-digit hexadecimal RGB format.
+         *
+         * @param {String} component
+         *     The hexadecimal string to shift or truncate to two digits.
+         *
+         * @returns {String}
+         *     A new 2-digit hexadecimal string containing the same digits as
+         *     the provided string, shifted or truncated as necessary to fit
+         *     within the 2-digit length limit.
+         */
+        var toHexComponent = function toHexComponent(component) {
+            return (component + '0').substring(0, 2).toUpperCase();
+        };
+
+        // Attempt to parse any non-RGB color as a named color
+        var groups = /^rgb:([0-9A-Fa-f]{1,4})\/([0-9A-Fa-f]{1,4})\/([0-9A-Fa-f]{1,4})$/.exec(color);
+        if (!groups)
+            return ColorScheme.NAMED_COLORS[color.toLowerCase()] || null;
+
+        // Convert to standard 6-digit hexadecimal RGB format
+        return '#' + toHexComponent(groups[1]) + toHexComponent(groups[2]) + toHexComponent(groups[3]);
+
+    };
+
+    /**
+     * Converts the given string representation of a color scheme which is
+     * supported by the Guacamole terminal emulator to a corresponding,
+     * intermediate ColorScheme object.
+     *
+     * @param {String} str
+     *     An arbitrary color scheme, in the string format supported by the
+     *     Guacamole terminal emulator.
+     *
+     * @returns {ColorScheme}
+     *     A new ColorScheme instance which represents the same color scheme as
+     *     the given string.
+     */
+    ColorScheme.fromString = function fromString(str) {
+
+        var scheme = new ColorScheme({ _originalString : str });
+
+        // For each semicolon-separated statement in the provided color scheme
+        var statements = str.split(/;/);
+        for (var i = 0; i < statements.length; i++) {
+
+            // Skip any statements which cannot be parsed
+            var statement = statements[i];
+            var groups = /^\s*(background|foreground|color([0-9]+))\s*:\s*(\S*)\s*$/.exec(statement);
+            if (!groups)
+                continue;
+
+            // If the statement is valid and contains a valid color, map that
+            // color to the appropriate property of the ColorScheme object
+            var color = toHexColor(groups[3]);
+            if (color) {
+                if (groups[1] === 'background')
+                    scheme.background = color;
+                else if (groups[1] === 'foreground')
+                    scheme.foreground = color;
+                else
+                    scheme.colors[parseInt(groups[2])] = color;
+            }
+
+        }
+
+        return scheme;
+
+    };
+
+    /**
+     * Returns whether the two given color schemes define the exact same
+     * colors.
+     *
+     * @param {ColorScheme} a
+     *     The first ColorScheme to compare.
+     *
+     * @param {ColorScheme} b
+     *     The second ColorScheme to compare.
+     *
+     * @returns {Boolean}
+     *     true if both color schemes contain the same colors, false otherwise.
+     */
+    ColorScheme.equals = function equals(a, b) {
+        return a.foreground === b.foreground
+            && a.background === b.background
+            && _.isEqual(a.colors, b.colors);
+    };
+
+    /**
+     * Converts the given ColorScheme to a string representation which is
+     * supported by the Guacamole terminal emulator.
+     *
+     * @param {ColorScheme} scheme
+     *     The ColorScheme to convert to a string.
+     *
+     * @returns {String}
+     *     The given color scheme, converted to the string format supported by
+     *     the Guacamole terminal emulator.
+     */
+    ColorScheme.toString = function toString(scheme) {
+
+        // Use originally-provided string if it equates to the exact same color scheme
+        if (!_.isUndefined(scheme._originalString) && ColorScheme.equals(scheme, ColorScheme.fromString(scheme._originalString)))
+            return scheme._originalString;
+
+        // Add background and foreground
+        var str = 'background: ' + fromHexColor(scheme.background) + ';\n'
+                + 'foreground: ' + fromHexColor(scheme.foreground) + ';';
+
+        // Add color definitions for each palette entry
+        for (var index in scheme.colors)
+            str += '\ncolor' + index + ': ' + fromHexColor(scheme.colors[index]) + ';';
+
+        return str;
+
+    };
+
+    /**
+     * The set of all named colors supported by the Guacamole terminal
+     * emulator and their corresponding 6-digit hexadecimal RGB
+     * representations. This set should contain all colors supported by xterm.
+     *
+     * @constant
+     * @type {Object.<String, String>}
+     */
+    ColorScheme.NAMED_COLORS = {
+        'aliceblue'             : '#F0F8FF',
+        'antiquewhite'          : '#FAEBD7',
+        'antiquewhite1'         : '#FFEFDB',
+        'antiquewhite2'         : '#EEDFCC',
+        'antiquewhite3'         : '#CDC0B0',
+        'antiquewhite4'         : '#8B8378',
+        'aqua'                  : '#00FFFF',
+        'aquamarine'            : '#7FFFD4',
+        'aquamarine1'           : '#7FFFD4',
+        'aquamarine2'           : '#76EEC6',
+        'aquamarine3'           : '#66CDAA',
+        'aquamarine4'           : '#458B74',
+        'azure'                 : '#F0FFFF',
+        'azure1'                : '#F0FFFF',
+        'azure2'                : '#E0EEEE',
+        'azure3'                : '#C1CDCD',
+        'azure4'                : '#838B8B',
+        'beige'                 : '#F5F5DC',
+        'bisque'                : '#FFE4C4',
+        'bisque1'               : '#FFE4C4',
+        'bisque2'               : '#EED5B7',
+        'bisque3'               : '#CDB79E',
+        'bisque4'               : '#8B7D6B',
+        'black'                 : '#000000',
+        'blanchedalmond'        : '#FFEBCD',
+        'blue'                  : '#0000FF',
+        'blue1'                 : '#0000FF',
+        'blue2'                 : '#0000EE',
+        'blue3'                 : '#0000CD',
+        'blue4'                 : '#00008B',
+        'blueviolet'            : '#8A2BE2',
+        'brown'                 : '#A52A2A',
+        'brown1'                : '#FF4040',
+        'brown2'                : '#EE3B3B',
+        'brown3'                : '#CD3333',
+        'brown4'                : '#8B2323',
+        'burlywood'             : '#DEB887',
+        'burlywood1'            : '#FFD39B',
+        'burlywood2'            : '#EEC591',
+        'burlywood3'            : '#CDAA7D',
+        'burlywood4'            : '#8B7355',
+        'cadetblue'             : '#5F9EA0',
+        'cadetblue1'            : '#98F5FF',
+        'cadetblue2'            : '#8EE5EE',
+        'cadetblue3'            : '#7AC5CD',
+        'cadetblue4'            : '#53868B',
+        'chartreuse'            : '#7FFF00',
+        'chartreuse1'           : '#7FFF00',
+        'chartreuse2'           : '#76EE00',
+        'chartreuse3'           : '#66CD00',
+        'chartreuse4'           : '#458B00',
+        'chocolate'             : '#D2691E',
+        'chocolate1'            : '#FF7F24',
+        'chocolate2'            : '#EE7621',
+        'chocolate3'            : '#CD661D',
+        'chocolate4'            : '#8B4513',
+        'coral'                 : '#FF7F50',
+        'coral1'                : '#FF7256',
+        'coral2'                : '#EE6A50',
+        'coral3'                : '#CD5B45',
+        'coral4'                : '#8B3E2F',
+        'cornflowerblue'        : '#6495ED',
+        'cornsilk'              : '#FFF8DC',
+        'cornsilk1'             : '#FFF8DC',
+        'cornsilk2'             : '#EEE8CD',
+        'cornsilk3'             : '#CDC8B1',
+        'cornsilk4'             : '#8B8878',
+        'crimson'               : '#DC143C',
+        'cyan'                  : '#00FFFF',
+        'cyan1'                 : '#00FFFF',
+        'cyan2'                 : '#00EEEE',
+        'cyan3'                 : '#00CDCD',
+        'cyan4'                 : '#008B8B',
+        'darkblue'              : '#00008B',
+        'darkcyan'              : '#008B8B',
+        'darkgoldenrod'         : '#B8860B',
+        'darkgoldenrod1'        : '#FFB90F',
+        'darkgoldenrod2'        : '#EEAD0E',
+        'darkgoldenrod3'        : '#CD950C',
+        'darkgoldenrod4'        : '#8B6508',
+        'darkgray'              : '#A9A9A9',
+        'darkgreen'             : '#006400',
+        'darkgrey'              : '#A9A9A9',
+        'darkkhaki'             : '#BDB76B',
+        'darkmagenta'           : '#8B008B',
+        'darkolivegreen'        : '#556B2F',
+        'darkolivegreen1'       : '#CAFF70',
+        'darkolivegreen2'       : '#BCEE68',
+        'darkolivegreen3'       : '#A2CD5A',
+        'darkolivegreen4'       : '#6E8B3D',
+        'darkorange'            : '#FF8C00',
+        'darkorange1'           : '#FF7F00',
+        'darkorange2'           : '#EE7600',
+        'darkorange3'           : '#CD6600',
+        'darkorange4'           : '#8B4500',
+        'darkorchid'            : '#9932CC',
+        'darkorchid1'           : '#BF3EFF',
+        'darkorchid2'           : '#B23AEE',
+        'darkorchid3'           : '#9A32CD',
+        'darkorchid4'           : '#68228B',
+        'darkred'               : '#8B0000',
+        'darksalmon'            : '#E9967A',
+        'darkseagreen'          : '#8FBC8F',
+        'darkseagreen1'         : '#C1FFC1',
+        'darkseagreen2'         : '#B4EEB4',
+        'darkseagreen3'         : '#9BCD9B',
+        'darkseagreen4'         : '#698B69',
+        'darkslateblue'         : '#483D8B',
+        'darkslategray'         : '#2F4F4F',
+        'darkslategray1'        : '#97FFFF',
+        'darkslategray2'        : '#8DEEEE',
+        'darkslategray3'        : '#79CDCD',
+        'darkslategray4'        : '#528B8B',
+        'darkslategrey'         : '#2F4F4F',
+        'darkturquoise'         : '#00CED1',
+        'darkviolet'            : '#9400D3',
+        'deeppink'              : '#FF1493',
+        'deeppink1'             : '#FF1493',
+        'deeppink2'             : '#EE1289',
+        'deeppink3'             : '#CD1076',
+        'deeppink4'             : '#8B0A50',
+        'deepskyblue'           : '#00BFFF',
+        'deepskyblue1'          : '#00BFFF',
+        'deepskyblue2'          : '#00B2EE',
+        'deepskyblue3'          : '#009ACD',
+        'deepskyblue4'          : '#00688B',
+        'dimgray'               : '#696969',
+        'dimgrey'               : '#696969',
+        'dodgerblue'            : '#1E90FF',
+        'dodgerblue1'           : '#1E90FF',
+        'dodgerblue2'           : '#1C86EE',
+        'dodgerblue3'           : '#1874CD',
+        'dodgerblue4'           : '#104E8B',
+        'firebrick'             : '#B22222',
+        'firebrick1'            : '#FF3030',
+        'firebrick2'            : '#EE2C2C',
+        'firebrick3'            : '#CD2626',
+        'firebrick4'            : '#8B1A1A',
+        'floralwhite'           : '#FFFAF0',
+        'forestgreen'           : '#228B22',
+        'fuchsia'               : '#FF00FF',
+        'gainsboro'             : '#DCDCDC',
+        'ghostwhite'            : '#F8F8FF',
+        'gold'                  : '#FFD700',
+        'gold1'                 : '#FFD700',
+        'gold2'                 : '#EEC900',
+        'gold3'                 : '#CDAD00',
+        'gold4'                 : '#8B7500',
+        'goldenrod'             : '#DAA520',
+        'goldenrod1'            : '#FFC125',
+        'goldenrod2'            : '#EEB422',
+        'goldenrod3'            : '#CD9B1D',
+        'goldenrod4'            : '#8B6914',
+        'gray'                  : '#BEBEBE',
+        'gray0'                 : '#000000',
+        'gray1'                 : '#030303',
+        'gray10'                : '#1A1A1A',
+        'gray100'               : '#FFFFFF',
+        'gray11'                : '#1C1C1C',
+        'gray12'                : '#1F1F1F',
+        'gray13'                : '#212121',
+        'gray14'                : '#242424',
+        'gray15'                : '#262626',
+        'gray16'                : '#292929',
+        'gray17'                : '#2B2B2B',
+        'gray18'                : '#2E2E2E',
+        'gray19'                : '#303030',
+        'gray2'                 : '#050505',
+        'gray20'                : '#333333',
+        'gray21'                : '#363636',
+        'gray22'                : '#383838',
+        'gray23'                : '#3B3B3B',
+        'gray24'                : '#3D3D3D',
+        'gray25'                : '#404040',
+        'gray26'                : '#424242',
+        'gray27'                : '#454545',
+        'gray28'                : '#474747',
+        'gray29'                : '#4A4A4A',
+        'gray3'                 : '#080808',
+        'gray30'                : '#4D4D4D',
+        'gray31'                : '#4F4F4F',
+        'gray32'                : '#525252',
+        'gray33'                : '#545454',
+        'gray34'                : '#575757',
+        'gray35'                : '#595959',
+        'gray36'                : '#5C5C5C',
+        'gray37'                : '#5E5E5E',
+        'gray38'                : '#616161',
+        'gray39'                : '#636363',
+        'gray4'                 : '#0A0A0A',
+        'gray40'                : '#666666',
+        'gray41'                : '#696969',
+        'gray42'                : '#6B6B6B',
+        'gray43'                : '#6E6E6E',
+        'gray44'                : '#707070',
+        'gray45'                : '#737373',
+        'gray46'                : '#757575',
+        'gray47'                : '#787878',
+        'gray48'                : '#7A7A7A',
+        'gray49'                : '#7D7D7D',
+        'gray5'                 : '#0D0D0D',
+        'gray50'                : '#7F7F7F',
+        'gray51'                : '#828282',
+        'gray52'                : '#858585',
+        'gray53'                : '#878787',
+        'gray54'                : '#8A8A8A',
+        'gray55'                : '#8C8C8C',
+        'gray56'                : '#8F8F8F',
+        'gray57'                : '#919191',
+        'gray58'                : '#949494',
+        'gray59'                : '#969696',
+        'gray6'                 : '#0F0F0F',
+        'gray60'                : '#999999',
+        'gray61'                : '#9C9C9C',
+        'gray62'                : '#9E9E9E',
+        'gray63'                : '#A1A1A1',
+        'gray64'                : '#A3A3A3',
+        'gray65'                : '#A6A6A6',
+        'gray66'                : '#A8A8A8',
+        'gray67'                : '#ABABAB',
+        'gray68'                : '#ADADAD',
+        'gray69'                : '#B0B0B0',
+        'gray7'                 : '#121212',
+        'gray70'                : '#B3B3B3',
+        'gray71'                : '#B5B5B5',
+        'gray72'                : '#B8B8B8',
+        'gray73'                : '#BABABA',
+        'gray74'                : '#BDBDBD',
+        'gray75'                : '#BFBFBF',
+        'gray76'                : '#C2C2C2',
+        'gray77'                : '#C4C4C4',
+        'gray78'                : '#C7C7C7',
+        'gray79'                : '#C9C9C9',
+        'gray8'                 : '#141414',
+        'gray80'                : '#CCCCCC',
+        'gray81'                : '#CFCFCF',
+        'gray82'                : '#D1D1D1',
+        'gray83'                : '#D4D4D4',
+        'gray84'                : '#D6D6D6',
+        'gray85'                : '#D9D9D9',
+        'gray86'                : '#DBDBDB',
+        'gray87'                : '#DEDEDE',
+        'gray88'                : '#E0E0E0',
+        'gray89'                : '#E3E3E3',
+        'gray9'                 : '#171717',
+        'gray90'                : '#E5E5E5',
+        'gray91'                : '#E8E8E8',
+        'gray92'                : '#EBEBEB',
+        'gray93'                : '#EDEDED',
+        'gray94'                : '#F0F0F0',
+        'gray95'                : '#F2F2F2',
+        'gray96'                : '#F5F5F5',
+        'gray97'                : '#F7F7F7',
+        'gray98'                : '#FAFAFA',
+        'gray99'                : '#FCFCFC',
+        'green'                 : '#00FF00',
+        'green1'                : '#00FF00',
+        'green2'                : '#00EE00',
+        'green3'                : '#00CD00',
+        'green4'                : '#008B00',
+        'greenyellow'           : '#ADFF2F',
+        'grey'                  : '#BEBEBE',
+        'grey0'                 : '#000000',
+        'grey1'                 : '#030303',
+        'grey10'                : '#1A1A1A',
+        'grey100'               : '#FFFFFF',
+        'grey11'                : '#1C1C1C',
+        'grey12'                : '#1F1F1F',
+        'grey13'                : '#212121',
+        'grey14'                : '#242424',
+        'grey15'                : '#262626',
+        'grey16'                : '#292929',
+        'grey17'                : '#2B2B2B',
+        'grey18'                : '#2E2E2E',
+        'grey19'                : '#303030',
+        'grey2'                 : '#050505',
+        'grey20'                : '#333333',
+        'grey21'                : '#363636',
+        'grey22'                : '#383838',
+        'grey23'                : '#3B3B3B',
+        'grey24'                : '#3D3D3D',
+        'grey25'                : '#404040',
+        'grey26'                : '#424242',
+        'grey27'                : '#454545',
+        'grey28'                : '#474747',
+        'grey29'                : '#4A4A4A',
+        'grey3'                 : '#080808',
+        'grey30'                : '#4D4D4D',
+        'grey31'                : '#4F4F4F',
+        'grey32'                : '#525252',
+        'grey33'                : '#545454',
+        'grey34'                : '#575757',
+        'grey35'                : '#595959',
+        'grey36'                : '#5C5C5C',
+        'grey37'                : '#5E5E5E',
+        'grey38'                : '#616161',
+        'grey39'                : '#636363',
+        'grey4'                 : '#0A0A0A',
+        'grey40'                : '#666666',
+        'grey41'                : '#696969',
+        'grey42'                : '#6B6B6B',
+        'grey43'                : '#6E6E6E',
+        'grey44'                : '#707070',
+        'grey45'                : '#737373',
+        'grey46'                : '#757575',
+        'grey47'                : '#787878',
+        'grey48'                : '#7A7A7A',
+        'grey49'                : '#7D7D7D',
+        'grey5'                 : '#0D0D0D',
+        'grey50'                : '#7F7F7F',
+        'grey51'                : '#828282',
+        'grey52'                : '#858585',
+        'grey53'                : '#878787',
+        'grey54'                : '#8A8A8A',
+        'grey55'                : '#8C8C8C',
+        'grey56'                : '#8F8F8F',
+        'grey57'                : '#919191',
+        'grey58'                : '#949494',
+        'grey59'                : '#969696',
+        'grey6'                 : '#0F0F0F',
+        'grey60'                : '#999999',
+        'grey61'                : '#9C9C9C',
+        'grey62'                : '#9E9E9E',
+        'grey63'                : '#A1A1A1',
+        'grey64'                : '#A3A3A3',
+        'grey65'                : '#A6A6A6',
+        'grey66'                : '#A8A8A8',
+        'grey67'                : '#ABABAB',
+        'grey68'                : '#ADADAD',
+        'grey69'                : '#B0B0B0',
+        'grey7'                 : '#121212',
+        'grey70'                : '#B3B3B3',
+        'grey71'                : '#B5B5B5',
+        'grey72'                : '#B8B8B8',
+        'grey73'                : '#BABABA',
+        'grey74'                : '#BDBDBD',
+        'grey75'                : '#BFBFBF',
+        'grey76'                : '#C2C2C2',
+        'grey77'                : '#C4C4C4',
+        'grey78'                : '#C7C7C7',
+        'grey79'                : '#C9C9C9',
+        'grey8'                 : '#141414',
+        'grey80'                : '#CCCCCC',
+        'grey81'                : '#CFCFCF',
+        'grey82'                : '#D1D1D1',
+        'grey83'                : '#D4D4D4',
+        'grey84'                : '#D6D6D6',
+        'grey85'                : '#D9D9D9',
+        'grey86'                : '#DBDBDB',
+        'grey87'                : '#DEDEDE',
+        'grey88'                : '#E0E0E0',
+        'grey89'                : '#E3E3E3',
+        'grey9'                 : '#171717',
+        'grey90'                : '#E5E5E5',
+        'grey91'                : '#E8E8E8',
+        'grey92'                : '#EBEBEB',
+        'grey93'                : '#EDEDED',
+        'grey94'                : '#F0F0F0',
+        'grey95'                : '#F2F2F2',
+        'grey96'                : '#F5F5F5',
+        'grey97'                : '#F7F7F7',
+        'grey98'                : '#FAFAFA',
+        'grey99'                : '#FCFCFC',
+        'honeydew'              : '#F0FFF0',
+        'honeydew1'             : '#F0FFF0',
+        'honeydew2'             : '#E0EEE0',
+        'honeydew3'             : '#C1CDC1',
+        'honeydew4'             : '#838B83',
+        'hotpink'               : '#FF69B4',
+        'hotpink1'              : '#FF6EB4',
+        'hotpink2'              : '#EE6AA7',
+        'hotpink3'              : '#CD6090',
+        'hotpink4'              : '#8B3A62',
+        'indianred'             : '#CD5C5C',
+        'indianred1'            : '#FF6A6A',
+        'indianred2'            : '#EE6363',
+        'indianred3'            : '#CD5555',
+        'indianred4'            : '#8B3A3A',
+        'indigo'                : '#4B0082',
+        'ivory'                 : '#FFFFF0',
+        'ivory1'                : '#FFFFF0',
+        'ivory2'                : '#EEEEE0',
+        'ivory3'                : '#CDCDC1',
+        'ivory4'                : '#8B8B83',
+        'khaki'                 : '#F0E68C',
+        'khaki1'                : '#FFF68F',
+        'khaki2'                : '#EEE685',
+        'khaki3'                : '#CDC673',
+        'khaki4'                : '#8B864E',
+        'lavender'              : '#E6E6FA',
+        'lavenderblush'         : '#FFF0F5',
+        'lavenderblush1'        : '#FFF0F5',
+        'lavenderblush2'        : '#EEE0E5',
+        'lavenderblush3'        : '#CDC1C5',
+        'lavenderblush4'        : '#8B8386',
+        'lawngreen'             : '#7CFC00',
+        'lemonchiffon'          : '#FFFACD',
+        'lemonchiffon1'         : '#FFFACD',
+        'lemonchiffon2'         : '#EEE9BF',
+        'lemonchiffon3'         : '#CDC9A5',
+        'lemonchiffon4'         : '#8B8970',
+        'lightblue'             : '#ADD8E6',
+        'lightblue1'            : '#BFEFFF',
+        'lightblue2'            : '#B2DFEE',
+        'lightblue3'            : '#9AC0CD',
+        'lightblue4'            : '#68838B',
+        'lightcoral'            : '#F08080',
+        'lightcyan'             : '#E0FFFF',
+        'lightcyan1'            : '#E0FFFF',
+        'lightcyan2'            : '#D1EEEE',
+        'lightcyan3'            : '#B4CDCD',
+        'lightcyan4'            : '#7A8B8B',
+        'lightgoldenrod'        : '#EEDD82',
+        'lightgoldenrod1'       : '#FFEC8B',
+        'lightgoldenrod2'       : '#EEDC82',
+        'lightgoldenrod3'       : '#CDBE70',
+        'lightgoldenrod4'       : '#8B814C',
+        'lightgoldenrodyellow'  : '#FAFAD2',
+        'lightgray'             : '#D3D3D3',
+        'lightgreen'            : '#90EE90',
+        'lightgrey'             : '#D3D3D3',
+        'lightpink'             : '#FFB6C1',
+        'lightpink1'            : '#FFAEB9',
+        'lightpink2'            : '#EEA2AD',
+        'lightpink3'            : '#CD8C95',
+        'lightpink4'            : '#8B5F65',
+        'lightsalmon'           : '#FFA07A',
+        'lightsalmon1'          : '#FFA07A',
+        'lightsalmon2'          : '#EE9572',
+        'lightsalmon3'          : '#CD8162',
+        'lightsalmon4'          : '#8B5742',
+        'lightseagreen'         : '#20B2AA',
+        'lightskyblue'          : '#87CEFA',
+        'lightskyblue1'         : '#B0E2FF',
+        'lightskyblue2'         : '#A4D3EE',
+        'lightskyblue3'         : '#8DB6CD',
+        'lightskyblue4'         : '#607B8B',
+        'lightslateblue'        : '#8470FF',
+        'lightslategray'        : '#778899',
+        'lightslategrey'        : '#778899',
+        'lightsteelblue'        : '#B0C4DE',
+        'lightsteelblue1'       : '#CAE1FF',
+        'lightsteelblue2'       : '#BCD2EE',
+        'lightsteelblue3'       : '#A2B5CD',
+        'lightsteelblue4'       : '#6E7B8B',
+        'lightyellow'           : '#FFFFE0',
+        'lightyellow1'          : '#FFFFE0',
+        'lightyellow2'          : '#EEEED1',
+        'lightyellow3'          : '#CDCDB4',
+        'lightyellow4'          : '#8B8B7A',
+        'lime'                  : '#00FF00',
+        'limegreen'             : '#32CD32',
+        'linen'                 : '#FAF0E6',
+        'magenta'               : '#FF00FF',
+        'magenta1'              : '#FF00FF',
+        'magenta2'              : '#EE00EE',
+        'magenta3'              : '#CD00CD',
+        'magenta4'              : '#8B008B',
+        'maroon'                : '#B03060',
+        'maroon1'               : '#FF34B3',
+        'maroon2'               : '#EE30A7',
+        'maroon3'               : '#CD2990',
+        'maroon4'               : '#8B1C62',
+        'mediumaquamarine'      : '#66CDAA',
+        'mediumblue'            : '#0000CD',
+        'mediumorchid'          : '#BA55D3',
+        'mediumorchid1'         : '#E066FF',
+        'mediumorchid2'         : '#D15FEE',
+        'mediumorchid3'         : '#B452CD',
+        'mediumorchid4'         : '#7A378B',
+        'mediumpurple'          : '#9370DB',
+        'mediumpurple1'         : '#AB82FF',
+        'mediumpurple2'         : '#9F79EE',
+        'mediumpurple3'         : '#8968CD',
+        'mediumpurple4'         : '#5D478B',
+        'mediumseagreen'        : '#3CB371',
+        'mediumslateblue'       : '#7B68EE',
+        'mediumspringgreen'     : '#00FA9A',
+        'mediumturquoise'       : '#48D1CC',
+        'mediumvioletred'       : '#C71585',
+        'midnightblue'          : '#191970',
+        'mintcream'             : '#F5FFFA',
+        'mistyrose'             : '#FFE4E1',
+        'mistyrose1'            : '#FFE4E1',
+        'mistyrose2'            : '#EED5D2',
+        'mistyrose3'            : '#CDB7B5',
+        'mistyrose4'            : '#8B7D7B',
+        'moccasin'              : '#FFE4B5',
+        'navajowhite'           : '#FFDEAD',
+        'navajowhite1'          : '#FFDEAD',
+        'navajowhite2'          : '#EECFA1',
+        'navajowhite3'          : '#CDB38B',
+        'navajowhite4'          : '#8B795E',
+        'navy'                  : '#000080',
+        'navyblue'              : '#000080',
+        'oldlace'               : '#FDF5E6',
+        'olive'                 : '#808000',
+        'olivedrab'             : '#6B8E23',
+        'olivedrab1'            : '#C0FF3E',
+        'olivedrab2'            : '#B3EE3A',
+        'olivedrab3'            : '#9ACD32',
+        'olivedrab4'            : '#698B22',
+        'orange'                : '#FFA500',
+        'orange1'               : '#FFA500',
+        'orange2'               : '#EE9A00',
+        'orange3'               : '#CD8500',
+        'orange4'               : '#8B5A00',
+        'orangered'             : '#FF4500',
+        'orangered1'            : '#FF4500',
+        'orangered2'            : '#EE4000',
+        'orangered3'            : '#CD3700',
+        'orangered4'            : '#8B2500',
+        'orchid'                : '#DA70D6',
+        'orchid1'               : '#FF83FA',
+        'orchid2'               : '#EE7AE9',
+        'orchid3'               : '#CD69C9',
+        'orchid4'               : '#8B4789',
+        'palegoldenrod'         : '#EEE8AA',
+        'palegreen'             : '#98FB98',
+        'palegreen1'            : '#9AFF9A',
+        'palegreen2'            : '#90EE90',
+        'palegreen3'            : '#7CCD7C',
+        'palegreen4'            : '#548B54',
+        'paleturquoise'         : '#AFEEEE',
+        'paleturquoise1'        : '#BBFFFF',
+        'paleturquoise2'        : '#AEEEEE',
+        'paleturquoise3'        : '#96CDCD',
+        'paleturquoise4'        : '#668B8B',
+        'palevioletred'         : '#DB7093',
+        'palevioletred1'        : '#FF82AB',
+        'palevioletred2'        : '#EE799F',
+        'palevioletred3'        : '#CD6889',
+        'palevioletred4'        : '#8B475D',
+        'papayawhip'            : '#FFEFD5',
+        'peachpuff'             : '#FFDAB9',
+        'peachpuff1'            : '#FFDAB9',
+        'peachpuff2'            : '#EECBAD',
+        'peachpuff3'            : '#CDAF95',
+        'peachpuff4'            : '#8B7765',
+        'peru'                  : '#CD853F',
+        'pink'                  : '#FFC0CB',
+        'pink1'                 : '#FFB5C5',
+        'pink2'                 : '#EEA9B8',
+        'pink3'                 : '#CD919E',
+        'pink4'                 : '#8B636C',
+        'plum'                  : '#DDA0DD',
+        'plum1'                 : '#FFBBFF',
+        'plum2'                 : '#EEAEEE',
+        'plum3'                 : '#CD96CD',
+        'plum4'                 : '#8B668B',
+        'powderblue'            : '#B0E0E6',
+        'purple'                : '#A020F0',
+        'purple1'               : '#9B30FF',
+        'purple2'               : '#912CEE',
+        'purple3'               : '#7D26CD',
+        'purple4'               : '#551A8B',
+        'rebeccapurple'         : '#663399',
+        'red'                   : '#FF0000',
+        'red1'                  : '#FF0000',
+        'red2'                  : '#EE0000',
+        'red3'                  : '#CD0000',
+        'red4'                  : '#8B0000',
+        'rosybrown'             : '#BC8F8F',
+        'rosybrown1'            : '#FFC1C1',
+        'rosybrown2'            : '#EEB4B4',
+        'rosybrown3'            : '#CD9B9B',
+        'rosybrown4'            : '#8B6969',
+        'royalblue'             : '#4169E1',
+        'royalblue1'            : '#4876FF',
+        'royalblue2'            : '#436EEE',
+        'royalblue3'            : '#3A5FCD',
+        'royalblue4'            : '#27408B',
+        'saddlebrown'           : '#8B4513',
+        'salmon'                : '#FA8072',
+        'salmon1'               : '#FF8C69',
+        'salmon2'               : '#EE8262',
+        'salmon3'               : '#CD7054',
+        'salmon4'               : '#8B4C39',
+        'sandybrown'            : '#F4A460',
+        'seagreen'              : '#2E8B57',
+        'seagreen1'             : '#54FF9F',
+        'seagreen2'             : '#4EEE94',
+        'seagreen3'             : '#43CD80',
+        'seagreen4'             : '#2E8B57',
+        'seashell'              : '#FFF5EE',
+        'seashell1'             : '#FFF5EE',
+        'seashell2'             : '#EEE5DE',
+        'seashell3'             : '#CDC5BF',
+        'seashell4'             : '#8B8682',
+        'sienna'                : '#A0522D',
+        'sienna1'               : '#FF8247',
+        'sienna2'               : '#EE7942',
+        'sienna3'               : '#CD6839',
+        'sienna4'               : '#8B4726',
+        'silver'                : '#C0C0C0',
+        'skyblue'               : '#87CEEB',
+        'skyblue1'              : '#87CEFF',
+        'skyblue2'              : '#7EC0EE',
+        'skyblue3'              : '#6CA6CD',
+        'skyblue4'              : '#4A708B',
+        'slateblue'             : '#6A5ACD',
+        'slateblue1'            : '#836FFF',
+        'slateblue2'            : '#7A67EE',
+        'slateblue3'            : '#6959CD',
+        'slateblue4'            : '#473C8B',
+        'slategray'             : '#708090',
+        'slategray1'            : '#C6E2FF',
+        'slategray2'            : '#B9D3EE',
+        'slategray3'            : '#9FB6CD',
+        'slategray4'            : '#6C7B8B',
+        'slategrey'             : '#708090',
+        'snow'                  : '#FFFAFA',
+        'snow1'                 : '#FFFAFA',
+        'snow2'                 : '#EEE9E9',
+        'snow3'                 : '#CDC9C9',
+        'snow4'                 : '#8B8989',
+        'springgreen'           : '#00FF7F',
+        'springgreen1'          : '#00FF7F',
+        'springgreen2'          : '#00EE76',
+        'springgreen3'          : '#00CD66',
+        'springgreen4'          : '#008B45',
+        'steelblue'             : '#4682B4',
+        'steelblue1'            : '#63B8FF',
+        'steelblue2'            : '#5CACEE',
+        'steelblue3'            : '#4F94CD',
+        'steelblue4'            : '#36648B',
+        'tan'                   : '#D2B48C',
+        'tan1'                  : '#FFA54F',
+        'tan2'                  : '#EE9A49',
+        'tan3'                  : '#CD853F',
+        'tan4'                  : '#8B5A2B',
+        'teal'                  : '#008080',
+        'thistle'               : '#D8BFD8',
+        'thistle1'              : '#FFE1FF',
+        'thistle2'              : '#EED2EE',
+        'thistle3'              : '#CDB5CD',
+        'thistle4'              : '#8B7B8B',
+        'tomato'                : '#FF6347',
+        'tomato1'               : '#FF6347',
+        'tomato2'               : '#EE5C42',
+        'tomato3'               : '#CD4F39',
+        'tomato4'               : '#8B3626',
+        'turquoise'             : '#40E0D0',
+        'turquoise1'            : '#00F5FF',
+        'turquoise2'            : '#00E5EE',
+        'turquoise3'            : '#00C5CD',
+        'turquoise4'            : '#00868B',
+        'violet'                : '#EE82EE',
+        'violetred'             : '#D02090',
+        'violetred1'            : '#FF3E96',
+        'violetred2'            : '#EE3A8C',
+        'violetred3'            : '#CD3278',
+        'violetred4'            : '#8B2252',
+        'webgray'               : '#808080',
+        'webgreen'              : '#008000',
+        'webgrey'               : '#808080',
+        'webmaroon'             : '#800000',
+        'webpurple'             : '#800080',
+        'wheat'                 : '#F5DEB3',
+        'wheat1'                : '#FFE7BA',
+        'wheat2'                : '#EED8AE',
+        'wheat3'                : '#CDBA96',
+        'wheat4'                : '#8B7E66',
+        'white'                 : '#FFFFFF',
+        'whitesmoke'            : '#F5F5F5',
+        'x11gray'               : '#BEBEBE',
+        'x11green'              : '#00FF00',
+        'x11grey'               : '#BEBEBE',
+        'x11maroon'             : '#B03060',
+        'x11purple'             : '#A020F0',
+        'yellow'                : '#FFFF00',
+        'yellow1'               : '#FFFF00',
+        'yellow2'               : '#EEEE00',
+        'yellow3'               : '#CDCD00',
+        'yellow4'               : '#8B8B00',
+        'yellowgreen'           : '#9ACD32'
+    };
+
+    return ColorScheme;
+
+}]);
diff --git a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css
index 8fe19d6..c5645fb 100644
--- a/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css
+++ b/guacamole/src/main/webapp/app/manage/styles/connection-parameter.css
@@ -29,6 +29,7 @@
     display: table;
     padding-left: .5em;
     border-left: 3px solid rgba(0,0,0,0.125);
+    width: 100%;
 }
 
 .connection-parameters .form .fields .labeled-field {
@@ -40,8 +41,11 @@
     display: table-cell;
     padding: 0.125em;
     vertical-align: top;
+    width: 100%;
 }
 
 .connection-parameters .form .fields .field-header {
     padding-right: 1em;
+    width: 0;
+    white-space: nowrap;
 }
diff --git a/guacamole/src/main/webapp/app/rest/types/Field.js b/guacamole/src/main/webapp/app/rest/types/Field.js
index 84dfe13..195db82 100644
--- a/guacamole/src/main/webapp/app/rest/types/Field.js
+++ b/guacamole/src/main/webapp/app/rest/types/Field.js
@@ -168,7 +168,16 @@
          *
          * @type String
          */
-        QUERY_PARAMETER : 'QUERY_PARAMETER'
+        QUERY_PARAMETER : 'QUERY_PARAMETER',
+
+        /**
+         * The type string associated with parameters that may contain color
+         * schemes accepted by the Guacamole server terminal emulator and
+         * protocols which leverage it.
+         *
+         * @type String
+         */
+        TERMINAL_COLOR_SCHEME : 'TERMINAL_COLOR_SCHEME'
 
     };
 
diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html
index e675546..309f114 100644
--- a/guacamole/src/main/webapp/index.html
+++ b/guacamole/src/main/webapp/index.html
@@ -27,6 +27,7 @@
         <link rel="icon" type="image/png" href="images/logo-64.png"/>
         <link rel="icon" type="image/png" sizes="144x144" href="images/logo-144.png"/>
         <link rel="apple-touch-icon" type="image/png" href="images/logo-144.png"/>
+        <link rel="stylesheet" type="text/css" href="webjars/simonwep__pickr/1.2.6/dist/themes/monolith.min.css"/>
         <link rel="stylesheet" type="text/css" href="app.css?v=${project.version}">
         <title ng-bind="page.title | translate"></title>
     </head>
@@ -87,7 +88,10 @@
 
         <!-- JSTZ -->
         <script type="text/javascript" src="webjars/jstz/1.0.10/dist/jstz.min.js"></script>
-        
+
+        <!-- Pickr (color picker) -->
+        <script type="text/javascript" src="webjars/simonwep__pickr/1.2.6/dist/pickr.es5.min.js"></script>
+
         <!-- Polyfills for the "datalist" element, Blob and the FileSaver API -->
         <script type="text/javascript" src="webjars/blob-polyfill/1.0.20150320/Blob.js"></script>
         <script type="text/javascript" src="webjars/datalist-polyfill/1.14.0/datalist-polyfill.min.js"></script>
diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json
index 8592c9e..7b29549 100644
--- a/guacamole/src/main/webapp/translations/en.json
+++ b/guacamole/src/main/webapp/translations/en.json
@@ -148,6 +148,22 @@
 
     },
 
+    "COLOR_SCHEME" : {
+
+        "ACTION_CANCEL"       : "@:APP.ACTION_CANCEL",
+        "ACTION_HIDE_DETAILS" : "Hide",
+        "ACTION_SAVE"         : "@:APP.ACTION_SAVE",
+        "ACTION_SHOW_DETAILS" : "Show",
+
+        "FIELD_HEADER_BACKGROUND" : "Background",
+        "FIELD_HEADER_FOREGROUND" : "Foreground",
+
+        "FIELD_OPTION_CUSTOM" : "Custom...",
+
+        "SECTION_HEADER_DETAILS" : "Details:"
+
+    },
+
     "DATA_SOURCE_DEFAULT" : {
         "NAME" : "Default (XML)"
     },