diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Field.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Field.java index 70b1b9e3a..91e4c6ffd 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Field.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/form/Field.java @@ -22,7 +22,6 @@ package org.glyptodon.guacamole.form; -import java.util.ArrayList; import java.util.Collection; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -38,44 +37,44 @@ public class Field { /** * All possible types of field. */ - public enum Type { + public static class Type { /** * A text field, accepting arbitrary values. */ - TEXT, + public static String TEXT = "TEXT"; /** * A username field. This field type generally behaves identically to * arbitrary text fields, but has semantic differences. */ - USERNAME, + public static String USERNAME = "USERNAME"; /** * A password field, whose value is sensitive and must be hidden. */ - PASSWORD, + public static String PASSWORD = "PASSWORD"; /** * A numeric field, whose value must contain only digits. */ - NUMERIC, + public static String NUMERIC = "NUMERIC"; /** * A boolean field, whose value is either blank or "true". */ - BOOLEAN, + public static String BOOLEAN = "BOOLEAN"; /** * An enumerated field, whose legal values are fully enumerated by a * provided, finite list. */ - ENUM, + public static String ENUM = "ENUM"; /** * A text field that can span more than one line. */ - MULTILINE + public static String MULTILINE = "MULTILINE"; } @@ -92,7 +91,7 @@ public class Field { /** * The type of this field. */ - private Type type; + private String type; /** * The value of this field, when checked. This is only applicable to @@ -123,31 +122,12 @@ public class Field { * @param type * The type of this field. */ - public Field(String name, String title, Type type) { + public Field(String name, String title, String type) { this.name = name; this.title = title; this.type = type; } - /** - * Creates a new BOOLEAN Parameter with the given name, title, and value. - * - * @param name - * The unique name to associate with this field. - * - * @param title - * The human-readable title to associate with this field. - * - * @param value - * The value that should be assigned to this field if enabled. - */ - public Field(String name, String title, String value) { - this.name = name; - this.title = title; - this.type = Type.BOOLEAN; - this.value = value; - } - /** * Creates a new ENUM Parameter with the given name, title, and options. * @@ -236,7 +216,7 @@ public class Field { * @return * The type of this field. */ - public Type getType() { + public String getType() { return type; } @@ -246,7 +226,7 @@ public class Field { * @param type * The type of this field. */ - public void setType(Type type) { + public void setType(String type) { this.type = type; } 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 new file mode 100644 index 000000000..e8a1045b3 --- /dev/null +++ b/guacamole/src/main/webapp/app/form/services/formService.js @@ -0,0 +1,275 @@ +/* + * 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. + */ + +/** + * A service for maintaining form-related metadata and linking that data to + * corresponding controllers and templates. + */ +angular.module('form').provider('formService', function formServiceProvider() { + + /** + * Reference to the provider itself. + * + * @type formServiceProvider + */ + var provider = this; + + /** + * Map of all registered field type definitions by name. + * + * @type Object. + */ + this.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. + * + * @param {String} fieldTypeName + * The name which uniquely identifies the field type being registered. + * + * @param {FieldType} fieldType + * The field type definition to associate with the given name. + */ + this.registerFieldType = function registerFieldType(fieldTypeName, fieldType) { + + // Store field type + provider.fieldTypes[fieldTypeName] = fieldType; + + }; + + // Factory method required by provider + this.$get = ['$injector', function formServiceFactory($injector) { + + // Required services + var $compile = $injector.get('$compile'); + var $http = $injector.get('$http'); + var $q = $injector.get('$q'); + var $templateCache = $injector.get('$templateCache'); + + var service = {}; + + service.fieldTypes = provider.fieldTypes; + + /** + * Returns a Promise which resolves with the HTML contents of the + * template at the given URL. The template contents will be retrieved from + * the $templateCache if possible. + * + * @param {String} url + * The URL of the template to retrieve. + * + * @returns {Promise.} + * A Promise which resolves with the HTML contents of the template at + * the given URL. + */ + var templateRequest = function templateRequest(url) { + + // Pull template from cache if present + var template = $templateCache.get(url); + if (template) + return $q.when(template); + + // Defer retrieval of template + var templateContent = $q.defer(); + + // Retrieve template manually + $http({ + method : 'GET', + url : url, + cache : true + }) + + // Upon success, resolve promise and update template cache + .success(function templateRetrieved(html) { + $templateCache.put(url, html); + templateContent.resolve(html); + }) + + // Fail if template cannot be retrieved + .error(function templateError() { + templateContent.reject(); + }); + + // Return promise which will resolve with the retrieved template + return templateContent.promise; + + }; + + /** + * Compiles and links the field associated with the given name to the given + * scope, producing a distinct and independent DOM Element which functions + * as an instance of that field. The scope object provided must include at + * least the following properties: + * + * namespace: + * A String which defines the unique namespace associated the + * translation strings used by the form using a field of this type. + * + * field: + * The Field object that is being rendered, representing a field of + * this type. + * + * model: + * The current String value of the field, if any. + * + * @param {String} fieldTypeName + * The name of the field type defining the nature of the element to be + * created. + * + * @param {Object} scope + * The scope to which the new element will be linked. + * + * @return {Promise.} + * A Promise which resolves to the compiled Element. If an error occurs + * while retrieving the field type, this Promise will be rejected. + */ + service.createFieldElement = function createFieldElement(fieldTypeName, scope) { + + // Ensure field type is defined + var fieldType = provider.fieldTypes[fieldTypeName]; + if (!fieldType) + return $q.reject(); + + // Populate scope using defined controller + if (fieldType.module && fieldType.controller) { + var $controller = angular.injector(['ng', fieldType.module]).get('$controller'); + $controller(fieldType.controller, {'$scope' : scope}); + } + + // Defer compilation of template pending successful retrieval + var compiledTemplate = $q.defer(); + + // Use raw HTML template if provided + if (fieldType.template) + compiledTemplate.resolve($compile(fieldType.template)(scope)); + + // If no raw HTML template is provided, retrieve template from URL + else { + + // Attempt to retrieve template HTML + templateRequest(fieldType.templateUrl) + + // Resolve with compiled HTML upon success + .then(function templateRetrieved(html) { + compiledTemplate.resolve($compile(html)(scope)); + }) + + // Reject on failure + ['catch'](function templateError() { + compiledTemplate.reject(); + }); + + } + + // Return promise which resolves to the compiled template + return compiledTemplate.promise; + + }; + + return service; + + }]; + +}); 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 diff --git a/guacamole/src/main/webapp/app/form/types/FieldType.js b/guacamole/src/main/webapp/app/form/types/FieldType.js new file mode 100644 index 000000000..5e94f61fd --- /dev/null +++ b/guacamole/src/main/webapp/app/form/types/FieldType.js @@ -0,0 +1,92 @@ +/* + * 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. + */ + +/** + * Service which defines the FieldType class. + */ +angular.module('form').factory('FieldType', [function defineFieldType() { + + /** + * The object used by the formService for describing field types. + * + * @constructor + * @param {FieldType|Object} [template={}] + * The object whose properties should be copied within the new + * FieldType. + */ + var FieldType = function FieldType(template) { + + // Use empty object by default + template = template || {}; + + /** + * The raw HTML of the template that should be injected into the DOM of + * a form using this field type. If provided, this will be used instead + * of templateUrl. + * + * @type String + */ + this.template = template.template; + + /** + * The URL of the template that should be injected into the DOM of a + * form using this field type. This property will be ignored if a raw + * HTML template is supplied via the template property. + * + * @type String + */ + this.templateUrl = template.templateUrl; + + /** + * The name of the AngularJS module defining the controller for this + * field type. This is optional, as not all field types will need + * controllers. + * + * @type String + */ + this.module = template.module; + + /** + * The name of the controller for this field type. This is optional, as + * not all field types will need controllers. If a controller is + * specified, it will receive the following properties on the scope: + * + * namespace: + * A String which defines the unique namespace associated the + * translation strings used by the form using a field of this type. + * + * field: + * The Field object that is being rendered, representing a field of + * this type. + * + * model: + * The current String value of the field, if any. + * + * @type String + */ + this.controller = template.controller; + + }; + + return FieldType; + +}]); \ No newline at end of file