From 48e10ab69a6e7b6f911608c8dbe7215ab9067924 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Jun 2015 00:09:52 -0700 Subject: [PATCH] GUAC-1176: Migrate all field types to the new field registration service. Use field registration service within guacFormField directive. --- .../controllers/checkboxFieldController.js | 40 +++++ .../form/controllers/numberFieldController.js | 40 +++++ .../controllers/passwordFieldController.js | 75 ++++++++ .../form/controllers/selectFieldController.js | 64 +++++++ .../webapp/app/form/directives/formField.js | 163 ++---------------- .../webapp/app/form/services/formService.js | 85 ++++++++- .../app/form/templates/checkboxField.html | 1 + .../webapp/app/form/templates/formField.html | 18 +- .../app/form/templates/numberField.html | 1 + .../app/form/templates/passwordField.html | 4 + .../app/form/templates/selectField.html | 1 + .../app/form/templates/textAreaField.html | 1 + .../webapp/app/form/templates/textField.html | 1 + 13 files changed, 330 insertions(+), 164 deletions(-) create mode 100644 guacamole/src/main/webapp/app/form/controllers/checkboxFieldController.js create mode 100644 guacamole/src/main/webapp/app/form/controllers/numberFieldController.js create mode 100644 guacamole/src/main/webapp/app/form/controllers/passwordFieldController.js create mode 100644 guacamole/src/main/webapp/app/form/controllers/selectFieldController.js create mode 100644 guacamole/src/main/webapp/app/form/templates/checkboxField.html create mode 100644 guacamole/src/main/webapp/app/form/templates/numberField.html create mode 100644 guacamole/src/main/webapp/app/form/templates/passwordField.html create mode 100644 guacamole/src/main/webapp/app/form/templates/selectField.html create mode 100644 guacamole/src/main/webapp/app/form/templates/textAreaField.html create mode 100644 guacamole/src/main/webapp/app/form/templates/textField.html diff --git a/guacamole/src/main/webapp/app/form/controllers/checkboxFieldController.js b/guacamole/src/main/webapp/app/form/controllers/checkboxFieldController.js new file mode 100644 index 000000000..eb15d37a5 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/controllers/checkboxFieldController.js @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * Controller for checkbox fields. + */ +angular.module('form').controller('checkboxFieldController', ['$scope', + function checkboxFieldController($scope) { + + // Update typed value when model is changed + $scope.$watch('model', function modelChanged(model) { + $scope.typedValue = (model === $scope.field.value); + }); + + // Update string value in model when typed value is changed + $scope.$watch('typedValue', function typedValueChanged(typedValue) { + $scope.model = (typedValue ? $scope.field.value : ''); + }); + +}]); diff --git a/guacamole/src/main/webapp/app/form/controllers/numberFieldController.js b/guacamole/src/main/webapp/app/form/controllers/numberFieldController.js new file mode 100644 index 000000000..fc213099e --- /dev/null +++ b/guacamole/src/main/webapp/app/form/controllers/numberFieldController.js @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * Controller for number fields. + */ +angular.module('form').controller('numberFieldController', ['$scope', + function numberFieldController($scope) { + + // Update typed value when model is changed + $scope.$watch('model', function modelChanged(model) { + $scope.typedValue = (model ? Number(model) : null); + }); + + // Update string value in model when typed value is changed + $scope.$watch('typedValue', function typedValueChanged(typedValue) { + $scope.model = (typedValue ? typedValue.toString() : ''); + }); + +}]); diff --git a/guacamole/src/main/webapp/app/form/controllers/passwordFieldController.js b/guacamole/src/main/webapp/app/form/controllers/passwordFieldController.js new file mode 100644 index 000000000..4b726f1a4 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/controllers/passwordFieldController.js @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * Controller for password fields. + */ +angular.module('form').controller('passwordFieldController', ['$scope', + function passwordFieldController($scope) { + + /** + * The type to use for the input field. By default, the input field will + * have the type 'password', and thus will be masked. + * + * @type String + * @default 'password' + */ + $scope.passwordInputType = 'password'; + + /** + * Returns a string which describes the action the next call to + * togglePassword() will have. + * + * @return {String} + * A string which describes the action the next call to + * togglePassword() will have. + */ + $scope.getTogglePasswordHelpText = function getTogglePasswordHelpText() { + + // If password is hidden, togglePassword() will show the password + if ($scope.passwordInputType === 'password') + return 'FORM.HELP_SHOW_PASSWORD'; + + // If password is shown, togglePassword() will hide the password + return 'FORM.HELP_HIDE_PASSWORD'; + + }; + + /** + * Toggles visibility of the field contents, if this field is a + * password field. Initially, password contents are masked + * (invisible). + */ + $scope.togglePassword = function togglePassword() { + + // If password is hidden, show the password + if ($scope.passwordInputType === 'password') + $scope.passwordInputType = 'text'; + + // If password is shown, hide the password + else + $scope.passwordInputType = 'password'; + + }; + +}]); diff --git a/guacamole/src/main/webapp/app/form/controllers/selectFieldController.js b/guacamole/src/main/webapp/app/form/controllers/selectFieldController.js new file mode 100644 index 000000000..593cc716b --- /dev/null +++ b/guacamole/src/main/webapp/app/form/controllers/selectFieldController.js @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/** + * Controller for select fields. + */ +angular.module('form').controller('selectFieldController', ['$scope', '$injector', + function selectFieldController($scope, $injector) { + + // Required services + var translationStringService = $injector.get('translationStringService'); + + /** + * Produces the translation string for the given field option + * value. The translation string will be of the form: + * + * NAMESPACE.FIELD_OPTION_NAME_VALUE + * + * where NAMESPACE is the namespace provided to the + * directive, NAME is the field name transformed + * via translationStringService.canonicalize(), and + * VALUE is the option value transformed via + * translationStringService.canonicalize() + * + * @param {String} value + * The name of the option value. + * + * @returns {String} + * The translation string which produces the translated name of the + * value specified. + */ + $scope.getFieldOption = function getFieldOption(value) { + + // If no field, or no value, then no corresponding translation string + if (!$scope.field || !$scope.field.name || !value) + return ''; + + return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') + + '.FIELD_OPTION_' + translationStringService.canonicalize($scope.field.name) + + '_' + translationStringService.canonicalize(value || 'EMPTY'); + + }; + +}]); diff --git a/guacamole/src/main/webapp/app/form/directives/formField.js b/guacamole/src/main/webapp/app/form/directives/formField.js index 96dedc890..a0fa2a7a3 100644 --- a/guacamole/src/main/webapp/app/form/directives/formField.js +++ b/guacamole/src/main/webapp/app/form/directives/formField.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -60,55 +60,19 @@ angular.module('form').directive('guacFormField', [function formField() { }, templateUrl: 'app/form/templates/formField.html', - controller: ['$scope', '$injector', function formFieldController($scope, $injector) { + controller: ['$scope', '$injector', '$element', function formFieldController($scope, $injector, $element) { // Required services + var formService = $injector.get('formService'); var translationStringService = $injector.get('translationStringService'); /** - * The type to use for password input fields. By default, password - * input fields have type 'password', and are thus masked. - * - * @type String - * @default 'password' - */ - $scope.passwordInputType = 'password'; - - /** - * Returns a string which describes the action the next call to - * togglePassword() will have. + * The element which should contain any compiled field content. The + * actual content of a field is dynamically determined by its type. * - * @return {String} - * A string which describes the action the next call to - * togglePassword() will have. + * @type Element[] */ - $scope.getTogglePasswordHelpText = function getTogglePasswordHelpText() { - - // If password is hidden, togglePassword() will show the password - if ($scope.passwordInputType === 'password') - return 'FORM.HELP_SHOW_PASSWORD'; - - // If password is shown, togglePassword() will hide the password - return 'FORM.HELP_HIDE_PASSWORD'; - - }; - - /** - * Toggles visibility of the field contents, if this field is a - * password field. Initially, password contents are masked - * (invisible). - */ - $scope.togglePassword = function togglePassword() { - - // If password is hidden, show the password - if ($scope.passwordInputType === 'password') - $scope.passwordInputType = 'text'; - - // If password is shown, hide the password - else - $scope.passwordInputType = 'password'; - - }; + var fieldContent = $element.find('.form-field'); /** * Produces the translation string for the header of the current @@ -135,113 +99,20 @@ angular.module('form').directive('guacFormField', [function formField() { }; - /** - * Produces the translation string for the given field option - * value. The translation string will be of the form: - * - * NAMESPACE.FIELD_OPTION_NAME_VALUE - * - * where NAMESPACE is the namespace provided to the - * directive, NAME is the field name transformed - * via translationStringService.canonicalize(), and - * VALUE is the option value transformed via - * translationStringService.canonicalize() - * - * @param {String} value - * The name of the option value. - * - * @returns {String} - * The translation string which produces the translated name of the - * value specified. - */ - $scope.getFieldOption = function getFieldOption(value) { + // Update field contents when field definition is changed + $scope.$watch('field', function setField(field) { - // If no field, or no value, then no corresponding translation string - if (!$scope.field || !$scope.field.name || !value) - return ''; + // Reset contents + fieldContent.innerHTML = ''; - return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE') - + '.FIELD_OPTION_' + translationStringService.canonicalize($scope.field.name) - + '_' + translationStringService.canonicalize(value || 'EMPTY'); - - }; - - /** - * Translates the given string field value into an appropriately- - * typed value as dictated by the attributes of the field, - * exposing that typed value within the scope as - * $scope.typedValue. - * - * @param {String} modelValue - * The current string value of the field. - */ - var setTypedValue = function setTypedValue(modelValue) { - - // Don't bother if the model is not yet defined - if (!$scope.field) - return; - - // Coerce numeric strings to numbers - if ($scope.field.type === 'NUMERIC') - $scope.typedValue = (modelValue ? Number(modelValue) : null); - - // Coerce boolean strings to boolean values - else if ($scope.field.type === 'BOOLEAN') - $scope.typedValue = (modelValue === $scope.field.value); - - // All other field types are represented internally as strings - else - $scope.typedValue = modelValue || ''; - - }; - - /** - * Translates the given typed field value into a string as dictated - * by the attributes of the field, assigning that string value to - * the model. - * - * @param {String|Number|Boolean} typedValue - * The current value of the field, as an appropriate JavaScript - * type. - */ - var setModelValue = function setModelValue(typedValue) { - - // Don't bother if the model is not yet defined - if (!$scope.field) - return; - - // Convert numeric values back into strings - if ($scope.field.type === 'NUMERIC') { - if (!typedValue) - $scope.model = ''; - else - $scope.model = typedValue.toString(); + // Append field content + if (field) { + formService.createFieldElement(field.type, $scope) + .then(function fieldElementCreated(element) { + fieldContent.append(element); + }); } - // Convert boolean values back into strings based on field description - else if ($scope.field.type === 'BOOLEAN') - $scope.model = (typedValue ? $scope.field.value : ''); - - // All other field types are already strings - else - $scope.model = typedValue || ''; - - }; - - // Update string value and re-assign to model when field is changed - $scope.$watch('field', function setField(field) { - setTypedValue($scope.model); - setModelValue($scope.typedValue); - }); - - // Update typed value when model is changed - $scope.$watch('model', function setModel(model) { - setTypedValue(model); - }); - - // Update string value in model when typed value is changed - $scope.$watch('typedValue', function typedValueChanged(typedValue) { - setModelValue(typedValue); }); }] // end controller diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js index 8126ad40d..9cfccd988 100644 --- a/guacamole/src/main/webapp/app/form/services/formService.js +++ b/guacamole/src/main/webapp/app/form/services/formService.js @@ -85,7 +85,90 @@ angular.module('form').factory('formService', ['$injector', * * @type Object. */ - service.fieldTypes = {}; + service.fieldTypes = { + + /** + * Text field type. + * + * @see {@link Field.Type.TEXT} + * @type FieldType + */ + 'TEXT' : { + templateUrl : 'app/form/templates/textField.html' + }, + + /** + * Numeric field type. + * + * @see {@link Field.Type.NUMERIC} + * @type FieldType + */ + 'NUMERIC' : { + module : 'form', + controller : 'numberFieldController', + templateUrl : 'app/form/templates/numberField.html' + }, + + /** + * Boolean field type. + * + * @see {@link Field.Type.BOOLEAN} + * @type FieldType + */ + 'BOOLEAN' : { + module : 'form', + controller : 'checkboxFieldController', + templateUrl : 'app/form/templates/checkboxField.html' + }, + + /** + * Username field type. Identical in principle to a text field, but may + * have different semantics. + * + * @see {@link Field.Type.USERNAME} + * @type FieldType + */ + 'USERNAME' : { + templateUrl : 'app/form/templates/textField.html' + }, + + /** + * Password field type. Similar to a text field, but the contents of + * the field are masked. + * + * @see {@link Field.Type.PASSWORD} + * @type FieldType + */ + 'PASSWORD' : { + module : 'form', + controller : 'passwordFieldController', + templateUrl : 'app/form/templates/passwordField.html' + }, + + /** + * Enumerated field type. The user is presented a finite list of values + * to choose from. + * + * @see {@link Field.Type.ENUM} + * @type FieldType + */ + 'ENUM' : { + module : 'form', + controller : 'selectFieldController', + templateUrl : 'app/form/templates/selectField.html' + }, + + /** + * Multiline field type. The user may enter multiple lines of text. + * + * @see {@link Field.Type.MULTILINE} + * @type FieldType + */ + 'MULTILINE' : { + templateUrl : 'app/form/templates/textAreaField.html' + } + + }; /** * Registers a new field type under the given name. diff --git a/guacamole/src/main/webapp/app/form/templates/checkboxField.html b/guacamole/src/main/webapp/app/form/templates/checkboxField.html new file mode 100644 index 000000000..ad9d8e006 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/checkboxField.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/templates/formField.html b/guacamole/src/main/webapp/app/form/templates/formField.html index e9ecced7b..a90eec41e 100644 --- a/guacamole/src/main/webapp/app/form/templates/formField.html +++ b/guacamole/src/main/webapp/app/form/templates/formField.html @@ -24,24 +24,8 @@ {{getFieldHeader() | translate}} - +
- - - - - - -
- -
-
- - - - - -
diff --git a/guacamole/src/main/webapp/app/form/templates/numberField.html b/guacamole/src/main/webapp/app/form/templates/numberField.html new file mode 100644 index 000000000..3d6312e20 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/numberField.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/templates/passwordField.html b/guacamole/src/main/webapp/app/form/templates/passwordField.html new file mode 100644 index 000000000..644566f8e --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/passwordField.html @@ -0,0 +1,4 @@ +
+ +
+
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/templates/selectField.html b/guacamole/src/main/webapp/app/form/templates/selectField.html new file mode 100644 index 000000000..9df1f0769 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/selectField.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/templates/textAreaField.html b/guacamole/src/main/webapp/app/form/templates/textAreaField.html new file mode 100644 index 000000000..082476f14 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/textAreaField.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/form/templates/textField.html b/guacamole/src/main/webapp/app/form/templates/textField.html new file mode 100644 index 000000000..e213bc20e --- /dev/null +++ b/guacamole/src/main/webapp/app/form/templates/textField.html @@ -0,0 +1 @@ + \ No newline at end of file