# Source: jala/code/Form.js

    //
    // Jala Project [http://opensvn.csie.org/traccgi/jala]
    //
    // Copyright 2004 ORF Online und Teletext GmbH
    //
    // Licensed 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.
    //
    // $Revision$
    // $LastChangedBy$
    // $LastChangedDate$
    // $HeadURL$
    //

    /**
     * @fileoverview This class can be used to render forms and to validate
     * and store user submits. Further types of form components can be added
     * by subclassing jala.Form.Component.Input.
     */

    // Define the global namespace for Jala modules
    if (!global.jala) {
       global.jala = {};
    }

    /**
     * HelmaLib dependencies
     */
    app.addRepository("modules/core/Object.js");
    app.addRepository("modules/helma/Html.js");
    app.addRepository("modules/helma/Http.js");

    /**
     * Jala dependencies
     */
    app.addRepository(getProperty("jala.dir", "modules/jala") + "/code/I18n.js");

    /**
     * Constructs a new Form instance
     * @class A class that renders forms, validates submitted form data and
     * stores the data in a specified object.
     * @param {String} name The name of the form
     * @param {Object} dataObj An optional object used to retrieve values
     * to display in the form input fields contained in this Form instance.
     * @returns A newly created Form instance
     * @constructor
     */
    jala.Form = function(name, dataObj) {

       /**
        * Private field containing the tracker used during validation
        * @type jala.Form.Tracker
        * @private
        */
       var tracker = undefined;

       /**
        * Private field containing all components of this form
        * @type Array
        * @private
        */
       var components = [];

       /**
        * Private field containing the CSS class name of this form instance.
        * @type String
        */
       var className;

       /**
        * Private field containing the default error message
        * @type String
        * @private
        */
       var errorMessage;

       /**
        * Readonly reference to the name of the form
        * @type String
        */
       this.name = name;     // for doc purposes only, readonly-access through the getter function
       this.__defineGetter__("name", function() {   return name;   });

       /**
        * Sets the data object which is being edited by this form. This object
        * is used to get the default values when first printing the form and
        * - if no other object is provided - receives the changed values in save.
        * @param {Object} dataObj The object which is being edited by this form.
        * @see #save
        */
       this.setDataObject = function(newDataObj) {
          dataObj = newDataObj;
          return;
       };

       /**
        * Returns the data object containing the values used
        * for rendering the form.
        * @returns The data object of this jala.Form instance
        */
       this.getDataObject = function() {
          return dataObj;
       };

       /**
        * The default getter function for this form. Unless a getter
        * is specified for the component, this function is called
        * to retrieve the original value of a field.
        * When called, the scope is set to the data object and
        * the name of the element is the sole argument.
        * @see jala.Form.Component.Input#getValue
        * @type Function
        */
       this.getter;      // for doc purposes only

       // that's where the value really is stored:
       var getter = jala.Form.propertyGetter;

       this.__defineGetter__("getter", function() {   return getter;   });
       this.__defineSetter__("getter", function(newGetter) {
          if (newGetter instanceof Function) {
             getter = newGetter;
          }
       });

       /**
        * The default setter function for this form. Unless a getter
        * is specified for the component, this function is called to
        * store the a value of a field.
        * When called, the scope is set to the data object and
        * the name and value of the element are provided as arguments.
        * @see jala.Form.Component.Input#setValue
        * @type Function
        */
       this.setter;      // for doc purposes only

       // that's where the value really is stored:
       var setter = jala.Form.propertySetter;

       this.__defineGetter__("setter", function() {   return setter;   });
       this.__defineSetter__("setter", function(newSetter) {
          if (newSetter instanceof Function) {
             setter = newSetter;
          }
       });

       var tracker = undefined;

       /**
        * Sets the tracker object this form instance uses for collecting
        * error messages and parsed values.
        * @param {jala.Form.Tracker} newTracker
        */
       this.setTracker = function(newTracker) {
          if (newTracker instanceof jala.Form.Tracker){
             tracker = newTracker;
          }
          return;
       };

       /**
        * Returns the tracker object this form instance uses for collecting
        * error messages and parsed values.
        * @returns tracker object
        * @type jala.Form.Tracker
        */
       this.getTracker = function() {
          return tracker;
       };

       /**
        * Contains the default component skin
        * @type Skin
        */
       this.componentSkin = createSkin("<% param.error prefix=' ' suffix='\n' %><% param.label prefix=' ' suffix='\n' %><% param.controls prefix=' ' suffix='\n' %><% param.help prefix=' ' suffix='\n' %>");

       /**
        * Contains a map of component objects.
        * @type Object
        */
       this.components = {};

       /**
        * Returns an array containing the components
        * of this jala.Form instance.
        * @returns The components of this jala.Form instance.
        * @type Array
        */
       this.listComponents = function() {
          return components;
       };

       /**
        * Adds a component to this jala.Form instance
        * @param {jala.Form.Component.Input} component
        */
       this.addComponent = function(component) {
          component.setForm(this);
          components.push(component);
          this.components[component.name] = component;
          return;
       };

       /**
        * Returns true if this instance of jala.Form contains at least
        * one component doing a file upload.
        * @see jala.Form.Component#containsFileUpload
        * @type Boolean
        */
       this.containsFileUpload = function() {
          for (var i=0; i<components.length; i++) {
             if (components[i].containsFileUpload() == true) {
                return true;
             }
          }
          return false;
       };

       /**
        * Returns the class name set for this form instance.
        * @returns class name
        * @type String
        */
       this.getClassName = function() {
          return className;
       };

       /**
        * Sets an extra classname for this form instance
        * @param {String} newClassName new classname
        */
       this.setClassName = function(newClassName) {
          className = newClassName;
          return;
       };

       /**
        * Returns the general error message printed above the form
        * if any of the components didn't validate.
        * @returns error message
        * @type String
        */
       this.getErrorMessage = function() {
          return errorMessage;
       };

       /**
        * Sets the general error message printed above the form if any
        * of the components didn't validate.
        * @param {String} newErrorMessage error message
        */
       this.setErrorMessage = function(newErrorMessage) {
          errorMessage = newErrorMessage;
          return;
       };

       /**
        * Returns true if this instance of jala.Form holds a jala.Form.Tracker
        * instance and at least one error has been set on this tracker.
        * @returns true if an error has been encountered.
        * @type Boolean
        */
       this.hasError = function() {
          if (tracker) {
             return tracker.hasError();
          }
          return false;
       };

       /**
        * If this instance of jala.Form holds a jala.Form.Tracker
        * instance it returns the number of components that didn't
        * validate.
        * @returns Number of components that didn't validate.
        * @type Number
        */
       this.countErrors = function() {
          if (tracker) {
             return tracker.countErrors();
          }
          return 0;
       };

       /**
        * Main constructor body
        */
       if (!dataObj) {
          dataObj = {};
       }

       return this;
    };

    /** @ignore */
    jala.Form.prototype.toString = function() {
       return "[jala.Form]";
    };

    /**
     * The HTML renderer used by jala.Form
     * @type helma.Html
     */
    jala.Form.html = new helma.Html();

    /**
     * Constant used by require function to define that a component
     * should not validate if userinput is shorter than a given length.
     * Value: "minlength"
     * @type String
     * @final
     */
    jala.Form.MINLENGTH     = "minlength";

    /**
     * Constant used by require function to define that a component
     * should not validate if userinput exceeds a maximum length.
     * Value: "maxlength"
     * @type String
     * @final
     */
    jala.Form.MAXLENGTH     = "maxlength";

    /**
     * Constant used by require function to define that a component
     * should validate only if the user did provide input.
     * Value: "require"
     * @type String
     * @final
     */
    jala.Form.REQUIRE       = "require";

    /**
     * Constant used by require function to define that a select or
     * radio component should validate only if the user input is contained
     * in the list of options provided.
     * Value: "checkoptions"
     * @type String
     * @final
     */
    jala.Form.CHECKOPTIONS  = "checkoptions";

    /**
     * Constant used by require function to define that a file upload
     * component should validate only if the file's content type is
     * in the list of allowed content types provided.
     * Value: "contenttype"
     * @type String
     * @final
     */
    jala.Form.CONTENTTYPE   = "contenttype";

    /**
     * Constant used by require function to define that an image upload
     * component should validate only if the image's width is less than
     * the value provided.
     * Value: "maxwidth"
     * @type String
     * @final
     */
    jala.Form.MAXWIDTH      = "maxwidth";

    /**
     * Constant used by require function to define that an image upload
     * component should validate only if the image's width is more than
     * the value provided.
     * Value: "minwidth"
     * @type String
     * @final
     */
    jala.Form.MINWIDTH      = "minwidth";

    /**
     * Constant used by require function to define that an image upload
     * component should validate only if the image's height is less than
     * the value provided.
     * Value: "maxheight"
     * @type String
     * @final
     */
    jala.Form.MAXHEIGHT     = "maxheight";

    /**
     * Constant used by require function to define that an image upload
     * component should validate only if the image's height is more than
     * the value provided.
     * Value: "min-height"
     * @type String
     * @final
     */
    jala.Form.MINHEIGHT     = "minheight";

    /**
     * Utility to set up the prototype, constructor, superclass and superconstructor
     * properties to support an inheritance strategy that can chain constructors and methods.
     * @param {Function} subClass the object which inherits superClass' functions
     * @param {Function} superClass the object to inherit
     */
    jala.Form.extend = function(subClass, superClass) {
       var f = function() {};
       f.prototype = superClass.prototype;

       subClass.prototype = new f();
       subClass.prototype.constructor = subClass;
       subClass.superClass = superClass.prototype;
       subClass.superConstructor = superClass;
       return;
    };

    /**
     * Parses a plain javascript object tree and configures a
     * new jala.Form instance according to the properties.
     * Propertynames are matched with constants and setter-functions,
     * the property "type" is used to create new component objects.
     * @param {Object} config object tree containing config
     * @returns A newly created jala.Form instance based on the config specified
     * @type jala.Form
     */
    jala.Form.create = function(config, dataObj) {
       if (!config || !config.name || !config.components) {
          return null;
       }
       var form = new jala.Form(config.name, dataObj);
       if (config.legend) {
          form.setLegend(config.legend);
       }
       if (config.className) {
          form.setClassName(config.className);
       }
       if (config.errorMessage) {
          form.setErrorMessage(config.errorMessage);
       }
       if (config.getter) {
          form.getter = config.getter;
       }
       if (config.setter) {
          form.setter = config.setter;
       }
       if (config.components) {
          jala.Form.createComponents(form, config.components);
       }
       return form;
    };

    /**
     * Parses an array of plain js objects and tries to create components.
     * @param {jala.Form | jala.Form.Component.Fieldset} container
     *        Object whose addComponent method is used for adding new components.
     * @param {Array} arr Array of plain javascript objects used as config.
     * @private
     */
    jala.Form.createComponents = function(container, arr) {
       var form = (container.form) ? container.form : container;
       var components = [];
       var element;
       for (var i=0; i<arr.length; i++) {
          element = arr[i];
          var clazzName = (element["type"]) ? element["type"].titleize() : "Input";
          var constr = jala.Form.Component[clazzName];
          if (!constr) {
             // invalid constructor:
             var logStr = "jala.Form encountered unknown component type " + element["type"] + " in config of form ";
             logStr += (container.form) ? container.form.name : container.name;
             app.log(logStr);
             continue;
          }
          var name = element.name;
          if (!name && element.label) {
             name = element.label.toAlphanumeric().toLowerCase();
          } else if (!name && constr == jala.Form.Component.Fieldset) {
             var str = "fieldset";
             while(container.components[str]) {
                str += "1";
             }
             name = str;
          } else if (!name) {
             // couldn't find a name for the component:
             var logStr = "jala.Form encountered component of type " + clazzName.toLowerCase() + " without name or label property in config of form ";
             logStr += (container.form) ? container.form.name : container.name;
             app.log(logStr);
             continue;
          }
          var component = new constr(name);
          component.setForm(form);
          for (var key in element) {
             switch(key) {
                case "name":
                case "type":
                   break;
                case "messages":
                   for (var msgName in element[key]) {
                      component.setMessage(msgName, element[key][msgName]);
                   }
                   break;
                case "components":
                   jala.Form.createComponents(component, element[key]);
                   break;
                case "getter":
                case "setter":
                case "validator":
                   component[key] = element[key];
                   break;
                case jala.Form.REQUIRE:
                   component.require(key, element[key]);
                   break;
                default:
                   // check if key matches a constant:
                   if (jala.Form[key.toUpperCase()] == key.toLowerCase()) {
                      component.require(key.toLowerCase(), element[key]);
                   } else {
                      // call setter functions for all fields from config object:
                      // note: String.prototype.titleize from the helma.core module
                      // would uppercase the first letter, but lowercases all ensuing
                      // characters (maxLength would become Maxlength).
                      // note: use try/catch to detect if the setter method really exists
                      // because a check using if(component[method]) would fail for
                      // inherited methods even though executing the inherited method works.
                      try {
                         component["set" + key.charAt(0).toUpperCase() + key.substring(1)](element[key]);
                      } catch (e) {
                         // invalid field for this component
                         app.log("jala.Form encountered unknown field " + key + " in config of form " + component.form.name);
                      }
                   }
                   break;
             }
          }
          container.addComponent(component);
       }
       return;
    };

    /**
     * Static validator function to test values for being a valid email address.
     * @param {String} name name of the property being validated.
     * @param {String} value value in form input
     * @param {Object} reqData the whole request-data-object,
               in case properties depend on each other
     * @param {jala.Form} formObj instance of jala.Form
     * @returns Error message or null
     * @type String
     */
    jala.Form.isEmail = function(name, value, reqData, formObj) {
       if (value && !value.trim().isEmail()) {
          return "Please enter a valid email address.";
       }
       return null;
    };

    /**
     * Static validator function to test values for being a valid url.
     * @param {String} name name of the property being validated.
     * @param {String} value value in form input
     * @param {Object} reqData the whole request-data-object,
               in case properties depend on each other
     * @param {jala.Form} formObj instance of jala.Form
     * @returns Error message or null
     * @type String
     */
    jala.Form.isUrl = function(name, value, reqData, formObj) {
       if (value && !helma.Http.evalUrl(value)) {
          return "Please enter a valid URL (web address).";
       }
       return null;
    };

    /**
     * Renders the opening form tag
     * @private
     */
    jala.Form.prototype.renderFormOpen = function() {
       var formAttr = {
          id     : this.createDomId(),
          name   : this.name,
          action : (req.action == "main") ? "" : req.action,
          method : "post"
       };
       var className = this.getClassName();
       if (className) {
          formAttr["class"] = className;
       }
       if (this.containsFileUpload()) {
          // if there is an upload element, use multipart-enctype
          formAttr.enctype = "multipart/form-data";
       }
       jala.Form.html.openTag("form", formAttr);
       return;
    };

    /**
     * Renders this form including all components to response.
     */
    jala.Form.prototype.render = function() {

       this.renderFormOpen();
       res.write("\n");

       // print optional general error message
       var errorMessage = this.getErrorMessage();
       if (this.hasError() && errorMessage) {
          jala.Form.html.element(
             "div",
             gettext(errorMessage, this.countErrors()),
             {id: this.createDomId("error"), "class": "formError"}
          );
          res.write("\n");
       }

       // loop through elements
       var components  = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].render();
       }

       jala.Form.html.closeTag("form");
       return;
    };

    /**
     * renders the form as a string
     * @returns rendered form
     * @type String
     */
    jala.Form.prototype.renderAsString = function(param) {
       res.push();
       this.render(param);
       return res.pop();
    };

    /**
     * Creates a DOM identifier based on the arguments passed. The
     * resulting Id will be prefixed with the name of the form.
     * All arguments will be chained using camel casing.
     * @returns The DOM Id
     * @type String
     */
    jala.Form.prototype.createDomId = function(/* [part1][, part2][, ...] */) {
       res.push();
       res.write(this.name.charAt(0).toLowerCase());
       res.write(this.name.substring(1));
       for (var i=0;i<arguments.length;i++) {
          if (arguments[i]) {
             res.write(arguments[i].charAt(0).toUpperCase());
             res.write(arguments[i].substring(1));
          }
       }
       return res.pop();
    };

    /**
     * Validates user input from a submitted form by calling each
     * component's validate method.
     * @param {Object} reqData Optional submitted form data. If not specified
     * req.data is used.
     * @returns tracker object with error fields set.
     * @type jala.Form.Tracker
     */
    jala.Form.prototype.validate = function(reqData) {
       var tracker = new jala.Form.Tracker(reqData || req.data);
       var components = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].validate(tracker);
       }
       this.setTracker(tracker);
       return tracker;
    };

    /**
     * Sets the parsed values on an object. By default the internally
     * stored tracker and data objects are used, but those may be
     * overridden here.
     * @param {jala.Form.Tracker} tracker (optional) tracker object
     *       holding parsed data from form input.
     * @param {Object} destObj (optional) object whose values will be changed.
     *       By default the dataObj passed to the constructor or to
     *       setDataObject is used.
     */
    jala.Form.prototype.save = function(tracker, destObj) {
       tracker = tracker || this.getTracker();
       destObj = destObj || this.getDataObject();
       var components = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].save(tracker, destObj);
       }
       return;
    };

    /**
     * Parses form input, applies check functions and stores the values
     * if the form does validate. Otherwise this method returns false
     * without saving so that the form can be reprinted with error messages.
     * @param {Object} reqData input from form
     * @param {Object} destObj object whose values should be chanegd
     * @returns False if one of the checks failed,
     *          true if the element was saved correctly.
     * @type Boolean
     */
    jala.Form.prototype.handle = function(reqData, destObj) {
       var tracker = this.validate(reqData);
       if (tracker.hasError()) {
          return false;
       } else {
          this.save(tracker, destObj);
          return true;
       }
    };

    /**
     * Renders the whole form to response
     */
    jala.Form.prototype.render_macro = function() {
       this.render();
       return;
    };

    /**
     * Returns the id (equal to the name) of the form
     * @returns The id of this Form instance
     * @type String
     */
    jala.Form.prototype.id_macro = function() {
       res.write(this.name);
       return;
    };

    /**
     * Returns the name (equal to the id) of the form
     * @returns The name of this Form instance
     * @type String
     */
    jala.Form.prototype.name_macro = function() {
       res.write(this.name);
       return;
    };

    /**
     * Returns the class name of the form
     * @returns The class name of this Form instance
     * @type String
     */
    jala.Form.prototype.class_macro = function() {
       var className = this.getClassName();
       if (className) {
          res.write(className);
       }
       return;
    };

    /**
     * Writes the form opening tag to response
     */
    jala.Form.prototype.open_macro = function() {
       this.renderFormOpen();
       return;
    };

    /**
     * Writes the form closing tag to response
     */
    jala.Form.prototype.close_macro = function() {
       jala.Form.html.closeTag("form");
       return;
    };

    /**
     * The abstract base class for all components.
     * @constructor
     */
    jala.Form.Component = function Component(name) {

       if (!name) {
          throw "jala.Form.Component: missing component name";
       }

       /**
        * The Form this component belongs to
        * @type jala.Form
        * @private
        */
       var form;

       /**
        * Private field containing the CSS class name of this component
        * @type String
        */
       var className;

       /**
        * Readonly reference to name of component
        * @type String
        */
       this.name;     // for doc purposes only, readonly-access is through the getter function
       this.__defineGetter__("name", function() {   return name;   });

       /**
        * Readonly reference to instance of jala.Form.
        * @type jala.Form
        */
       this.form;     // for doc purposes only, readonly-access through the getter function
       this.__defineGetter__("form", function() {   return form;   });

       /**
        * Attaches this component to an instance of jala.Form.
        * @param {jala.Form} newForm form object
        * @private
        */
       this.setForm = function(newForm) {
          form = newForm;
          return;
       };

       /**
        * Returns the type of component. This is the lowercase'd name of the
        * constructor function.
        * @type String
        */
       this.getType = function() {
          return this.constructor.name.toLowerCase();
       };

       /**
        * Returns the class name set for this component.
        * @returns class name
        * @type String
        */
       this.getClassName = function() {
          return className;
       };

       /**
        * Sets an extra classname for this component
        * @param {String} newClassName new classname
        */
       this.setClassName = function(newClassName) {
          className = newClassName;
          return;
       };

       /**
        * Function defining wheter a component contains a file upload or not.
        * This value is used to render a form tag with the attribute
        * enctype=multipart/form-data.
        * Subclasses of jala.Form.Component that use a file upload element,
        * have to override this function and let it return true.
        * @type Boolean
        */
       this.containsFileUpload = function() {
          return false;
       };

       return this;
    };

    /** @ignore */
    jala.Form.Component.prototype.toString = function() {
       return "[" + this.constructor.name + " component '" + this.name + "']";
    };

    /**
     * Creates a DOM identifier based on the name of the form,
     * the name of the component and an additional string.
     * The items will be chained using camel casing.
     * @param {String} idPart Optional string appended to component's id.
     * @returns The DOM Id
     * @type String
     */
    jala.Form.Component.prototype.createDomId = function(idPart) {
       return this.form.createDomId(this.name, idPart);
    }

    /**
     * Function to render a component.
     * Subclasses of jala.Form.Component may override this function.
     */
    jala.Form.Component.prototype.render = function() {
       return;
    };

    /**
     * Function to validate a component.
     * Subclasses of jala.Form.Component may override this function.
     * @param {jala.Form.Tracker} tracker object tracking errors and holding
     *    parsed values and request data.
     */
    jala.Form.Component.prototype.validate = function(tracker) {
       return tracker;
    };

    /**
     * Function to save the data of a component.
     * Subclasses of jala.Form.Component may override this function.
     */
    jala.Form.Component.prototype.save = function(destObj, val) {
       return;
    };

    /**
     * Constructs a new Fieldset instance
     * @class Instances of this class represent a form fieldset containing
     * numerous form components
     * @param {String} name The name of the fieldset
     * @returns A newly created Fieldset instance
     * @constructor
     */
    jala.Form.Component.Fieldset = function Fieldset(name) {
       jala.Form.Component.Fieldset.superConstructor.apply(this, arguments);

       /**
        * Private field containing the components of this fieldset
        * @type Array
        * @private
        */
       var components = [];

       /**
        * Private field containing the legend of this fieldset
        * @type String
        * @private
        */
       var legend;

       /**
        * Contains a map of all component objects of this fieldset
        */
       this.components = {};

       /**
        * Returns an array containing the components
        * of this jala.Form.Component.Fieldset instance.
        * @returns The components of this jala.Form instance.
        * @type Array
        */
       this.listComponents = function() {
          return components;
       };

       /**
        * Adds a component to this jala.Form.Component.Fieldset instance
        * @param {jala.Form.Component.Input} component
        */
       this.addComponent = function(component) {
          component.setForm(this.form);
          components.push(component);
          this.components[component.name] = component;
          return;
       };

       /**
        * Returns true if this instance of jala.Form.Component.Fieldset
        * contains at least one component doing a file upload.
        * @see jala.Form.Component#containsFileUpload
        * @type Boolean
        */
       this.containsFileUpload = function() {
          for (var i=0; i<components.length; i++) {
             if (components[i].containsFileUpload() == true) {
                return true;
             }
          }
          return false;
       };

       /**
        * Returns the legend of the fieldset.
        * @returns legend
        * @type String
        */
       this.getLegend = function() {
          return legend;
       };

       /**
        * Sets the legend text.
        * @param {String} newLegend legend to use when printing the fieldset.
        */
       this.setLegend = function(newLegend) {
          legend = newLegend;
          return;
       };

       /**
        * Attaches this fieldset and all components to an instance of
        * jala.Form.
        * @param {jala.Form} newForm form object
        * @private
        */
       this.setForm = function(newForm) {
          form = newForm;
          for (var i=0; i<components.length; i++) {
             components[i].setForm(newForm);
          }
          return;
       };

    };
    // extend jala.Form.Component
    jala.Form.extend(jala.Form.Component.Fieldset, jala.Form.Component);

    /**
     * Renders all components within the fieldset.
     */
    jala.Form.Component.Fieldset.prototype.render = function() {

       var attr = {};
       var className = this.getClassName();
       if (className) {
          attr["class"] = className;
       }
       jala.Form.html.openTag("fieldset", attr);
       res.write("\n");

       // optional legend
       var legend = this.getLegend();
       if (legend != null) {
          res.write(" ");
          jala.Form.html.element("legend", legend);
          res.write("\n");
       }

       // loop through elements
       var components  = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].render();
       }

       jala.Form.html.closeTag("fieldset");
       res.write("\n");
       return;
    };

    /**
     * Validates all components within the fieldset.
     * @param {jala.Form.Tracker} tracker
     */
    jala.Form.Component.Fieldset.prototype.validate = function(tracker) {
       var components  = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].validate(tracker);
       }
       return;
    };

    /**
     * Saves all components within the fieldset.
     * @param {jala.Form.Tracker} tracker
     * @param {Object} destObj
     */
    jala.Form.Component.Fieldset.prototype.save = function(tracker, destObj) {
       var components  = this.listComponents();
       for (var i=0; i<components.length; i++) {
          components[i].save(tracker, destObj);
       }
       return;
    };

    /**
     * @class Subclass of jala.Form.Component that allows rendering a skin
     * within a form.
     * @base jala.Form.Component
     * @param {String} name The name of the component, used as the name of the skin
     * @returns A newly created Skin component instance
     * @constructor
     */
    jala.Form.Component.Skin = function Skin(name) {
       jala.Form.Component.Skin.superConstructor.apply(this, arguments);

       /**
        * Private field containing the handler object
        */
       var handler = undefined;

       /**
        * Returns the handler object for the skin.
        * @type Object
        */
       this.getHandler = function() {
          return handler;
       };

       /**
        * Sets the handler to use when rendering the skin.
        * By default, the form's data object is used a handler.
        * @param {Object} newHandler new skin handler object.
        */
       this.setHandler = function(newHandler) {
          handler = newHandler;
          return;
       };

       return this;
    };
    // extend jala.Form.Component
    jala.Form.extend(jala.Form.Component.Skin, jala.Form.Component);

    /**
     * Renders the skin named by this component to the response.
     */
    jala.Form.Component.Skin.prototype.render = function() {
       var handler = this.getHandler() || this.form.getDataObject();
       if (handler != null && handler instanceof HopObject) {
          handler.renderSkin(this.name, this);
       } else {
          res.write("Skin component '" + this.name + "' unhandled");
       }
       return;
    };

    /**
     * Creates a new input component instance.
     * @class Instances of this class represent a single form input field.
     * @param {String} name Name of the component, used as name of the html control.
     * @constructor
     */
    jala.Form.Component.Input = function Input(name) {
       jala.Form.Component.Input.superConstructor.apply(this, arguments);

       /**
        * Private map containing the requirements that need to be met
        */
       var requirements = {};

       /**
        * Private map containing messages to use when a requirement is not met
        */
       var messages = {};

       /**
        * Private field containing the label of this component
        * @type String
        */
       var label;

       /**
        * Private field containing the help text of this component
        * @type String
        */
       var help;

       /**
        * Sets a requirement for this component.
        * If function is called without arguments, jala.Form.REQUIRE
        * is set to true.
        * @param {String} key String defining the type of requirement,
        *             constants in jala.Form may be used.
        * @param {Object} val Value of the requirement.
        * @param {String} msg Optional error message if requirement
        *             is not fulfilled.
        */
       this.require = function(key, val, msg) {
          if (arguments.length == 0) {
             // set default value for arguments
             key = jala.Form.REQUIRE;
             val = true;
          }
          requirements[key] = val;
          if (msg) {
             this.setMessage(key, msg);
          }
          return;
       };

       /**
        * Returns the requirement value for a given key.
        * @param {String} key String defining the type of requirement,
        *             constants in jala.Form may be used.
        * @type Object
        */
       this.getRequirement = function(key) {
          return requirements[key];
       };

       /**
        * Sets a custom error message
        * @param {String} key String defining the type of requirement,
        *             constants in jala.Form may be used.
        * @param {String} msg Error message
        */
       this.setMessage = function(key, msg) {
          messages[key] = msg;
          return;
       };

       /**
        * Returns a specific message for a config element.
        * @param {String} key The key of the message as defined by
        *          the constants in jala.Form.* (e.g. "require",
        *          "maxlength", "minlength" ...
        * @param {String} defaultMsg the message to use when no message
        *          was defined.
        * @param {Object} args One or more arguments passed to the gettext
        * message processor which will replace {0}, {1} etc.
        * @returns rendered message
        * @type String
        */
       this.getMessage = function(key, defaultMsg, args) {
          var arr = [(messages[key]) ? messages[key] : defaultMsg];
          for (var i=2; i<arguments.length; i++) {
             arr.push(arguments[i]);
          }
          return gettext.apply(null, arr);
       };

       /**
        * Returns the label set for this component.
        * @returns label
        * @type String
        */
       this.getLabel = function() {
          return label;
       };

       /**
        * Sets the label for this component
        * @param {String} newLabel new label
        */
       this.setLabel = function(newLabel) {
          label = newLabel;
          return;
       };

       /**
        * Returns the help text set for this component.
        * @returns help text
        * @type String
        */
       this.getHelp = function() {
          return help;
       };

       /**
        * Sets the help text for this component
        * @param {String} newHelp new help text
        */
       this.setHelp = function(newHelp) {
          help = newHelp;
          return;
       };

       /**
        * The getter function for this component. If set, the
        * function is called to retrieve the original value of the
        * field. When called, the scope is set to the data object and
        * the name of the element is the sole argument.
        * @see #getValue
        * @type Function
        */
       this.getter;      // for doc purposes only

       // that's where the values really are stored:
       var getter, setter, validator;

       this.__defineGetter__("getter", function() {   return getter;   });
       this.__defineSetter__("getter", function(newGetter) {
          if (newGetter instanceof Function) {
             getter = newGetter;
          } else {
             throw "Invalid argument: getter must be a function";
          }
          return;
       });

       /**
        * The setter function for this component. If set, the
        * function is called to store the new value of the
        * field. When called, the scope is set to the data object and
        * the name and value of the element are provided as arguments.
        * @see #setValue
        * @type Function
        */
       this.setter;      // for doc purposes only

       this.__defineGetter__("setter", function() {   return setter;   });
       this.__defineSetter__("setter", function(newSetter) {
          if (newSetter instanceof Function) {
             setter = newSetter;
          } else {
             throw "Invalid argument: setter must be a function";
          }
          return;
       });

       /**
        * The validator function for this component. If set, the
        * function is called with the scope set to the data object
        * and with four arguments:
        * <li>the name of the element</li>
        * <li>the parsed value of the element if all requirements have
        *     been fulfilled. E.g., for a date editor, the parsed value would
        *     be a date object.</li>
        * <li>the map containing all user inputs as string (req.data)</li>
        * <li>the form object</li>
        * @see #validate
        * @type Function
        */
       this.validator;

       this.__defineGetter__("validator", function() {   return validator;   });
       this.__defineSetter__("validator", function(newValidator) {
          if (newValidator instanceof Function) {
             validator = newValidator;
          } else {
             throw "Invalid argument: validator must be a function";
          }
          return;
       });

       return this;
    };
    // extend jala.Form.Component
    jala.Form.extend(jala.Form.Component.Input, jala.Form.Component);

    /**
     * Validates the input provided to this component. First,
     * checkRequirements is called. If no error occurs, the input
     * is parsed using parseValue and passed on to the validator
     * function.
     * @see #checkRequirements
     * @see #parseValue
     * @param {jala.Form.Tracker} tracker Tracker object collecting
     *       request data, error messages and parsed values.
     */
    jala.Form.Component.Input.prototype.validate = function(tracker) {
       var error = this.checkRequirements(tracker.reqData);
       if (error != null) {
          tracker.errors[this.name] = error;
       } else {
          tracker.values[this.name] = this.parseValue(tracker.reqData);
          if (this.validator) {
             error = this.validator.call(
                this.form.getDataObject(),
                this.name,
                tracker.values[this.name],
                tracker.reqData,
                this.form
             );
             if (error != null) {
                tracker.errors[this.name] = error;
             }
          }
       }
       return;
    };

    /**
     * Saves the parsed value using setValue.
     * @see #setValue
     * @param {jala.Form.Tracker} tracker Tracker object collecting
     *       request data, error messages and parsed values.
     * @param {Object} destObj (optional) object whose values will be changed.
     */
    jala.Form.Component.Input.prototype.save = function(tracker, destObj) {
       this.setValue(destObj, tracker.values[this.name]);
       return;
    };

    /**
     * Retrieves the property which is edited by this component.
     * <ul>
     * <li>If no getter is given, the method returns the primitive property
     *    of the data object with the same name as the component.</li>
     * <li>If a getter function is defined, it is executed with the scope
     *    of the data object and the return value is used as default value.
     *    The name of the component is passed to the getter function
     *    as an argument.</li>
     * </ul>
     * @returns The value of the property
     * @type String|Number|Date
     */
    jala.Form.Component.Input.prototype.getValue = function() {
       if (this.form.getTracker()) {
          // handling re-rendering
          return null;
       } else {
          var getter = (this.getter) ? this.getter : this.form.getter;
          return getter.call(this.form.getDataObject(), this.name);
       }
    };

    /**
     * Sets a property of the object passed as argument to the given value.
     * <li>If no setter is set at the component, the primitive property
     *    of the data object is changed.</li>
     * <li>If a setter function is defined it is executed with the data object
     *    as scope and with the name and new value provided as arguments</li>
     * <li>If the setter is explicitly set to null, no changes are made at all.</li>
     * @param {Object} destObj (optional) object whose values will be changed.
     * @param {Object} value The value to set the property to
     * @returns True in case the update was successful, false otherwise.
     * @see jala.Form#setter
     */
    jala.Form.Component.Input.prototype.setValue = function(destObj, value) {
       // default value for this.setter is undefined, so if it has been
       // set to explicitly null, we don't save the value. in this case,
       // we assume, the property is handled outside of jala.Form or purposely
       // ignored at all.
       if (this.setter !== null) {
          var setter = (this.setter) ? this.setter : this.form.setter;
          setter.call(destObj, this.name, value);
       }
       return;
    };

    /**
     * Renders this component including label, error and help messages directly
     * to response.
     */
    jala.Form.Component.Input.prototype.render = function() {
       var className = (this.getRequirement(jala.Form.REQUIRE) == true) ? "require" : "optional";
       if (this.getClassName()) {
          className += " " + this.getClassName();
       }
       var tracker = this.form.getTracker();
       if (tracker && tracker.errors[this.name]) {
          className += " error";
       }

       jala.Form.html.openTag("div",
          {id: this.createDomId(),
           "class": "component " + className}
       );
       res.write("\n");
       renderSkin(this.form.componentSkin, this);
       jala.Form.html.closeTag("div");
       res.write("\n");
       return;
    };

    /**
     * If the error tracker holds an error message for this component,
     * it is wrapped in a div-tag and returned as a string.
     * @returns Rendered string
     * @type String
     */
    jala.Form.Component.Input.prototype.renderError = function() {
       var tracker = this.form.getTracker();
       if (tracker && tracker.errors[this.name]) {
          return jala.Form.html.elementAsString("div",
             tracker.errors[this.name],
             {"class": "errorText"}
          );
       }
       return null;
    };

    /**
     * Returns the rendered label of this component
     * @returns The rendered label of this component
     * @type String
     */
    jala.Form.Component.Input.prototype.renderLabel = function() {
       return jala.Form.html.elementAsString(
          "label",
          this.getLabel() || "",
          {"for": this.createDomId("control")}
       );
    };

    /**
     * If this component contains a help message, it is wrapped in
     * a div-tag and returned as a string.
     * @returns The rendered help message
     * @type String
     */
    jala.Form.Component.Input.prototype.renderHelp = function() {
       var help = this.getHelp();
       if (help) {
          return jala.Form.html.elementAsString(
             "div",
             help,
             {"class": "helpText"}
          );
       }
       return null;
    };

    /**
     * Renders this component including label, error and help messages
     * directly to response
     */
    jala.Form.Component.Input.prototype.render_macro = function() {
       this.render();
       return;
    };

    /**
     * Renders the control(s) of this component
     */
    jala.Form.Component.Input.prototype.controls_macro = function() {
       var attr = this.getControlAttributes();
       var tracker = this.form.getTracker()
       if (tracker) {
          this.renderControls(attr, null, tracker.reqData);
       } else {
          this.renderControls(attr, this.getValue());
       }
       return;
    };

    /**
     * Renders this component's error message (if set) directly to response
     */
    jala.Form.Component.Input.prototype.error_macro = function() {
       res.write(this.renderError());
       return;
    };

    /**
     * Renders this component's label.
     */
    jala.Form.Component.Input.prototype.label_macro = function() {
       res.write(this.renderLabel());
       return;
    };

    /**
     * Renders this component's help text, if set.
     */
    jala.Form.Component.Input.prototype.help_macro = function() {
       res.write(this.renderHelp());
       return;
    };

    /**
     * Renders this component's id
     * @see jala.Form#createDomId
     */
    jala.Form.Component.Input.prototype.id_macro = function() {
       res.write(this.createDomId());
       return;
    };

    /**
     * Renders this component's name
     */
    jala.Form.Component.Input.prototype.name_macro = function() {
       res.write(this.name);
       return;
    };

    /**
     * Renders this component's type
     */
    jala.Form.Component.Input.prototype.type_macro = function() {
       res.write(this.getType());
       return;
    };

    /**
     * Renders this component's class name.
     * Note that this is just the class name that has been explicitly
     * assigned using setClassName.
     * @see #setClassName
     */
    jala.Form.Component.Input.prototype.class_macro = function() {
       var className = this.getClassName();
       if (className) {
          res.write(className);
       }
       return;
    };

    /**
     * Creates a new attribute object for this element.
     * @returns Object with properties id, name, class
     * @type Object
     */
    jala.Form.Component.Input.prototype.getControlAttributes = function() {
       var attr = {
          id: this.createDomId("control"),
          name: this.name,
          "class": this.getType()
       };
       if (this.getClassName()) {
          attr["class"] += " " + this.getClassName();
       }
       return attr;
    };

    /**
     * Checks user input for maximum length, minimum length and require
     * if the corresponding options have been set using the require method.
     * @param {Object} reqData request data
     * @returns String containing error message or null if everything is ok.
     * @type String
     * @see #require
     */
    jala.Form.Component.Input.prototype.checkLength = function(reqData) {
       var require   = this.getRequirement(jala.Form.REQUIRE);
       var minLength = this.getRequirement(jala.Form.MINLENGTH);
       var maxLength = this.getRequirement(jala.Form.MAXLENGTH);

       if (require && (reqData[this.name] == null || reqData[this.name].trim() == "")) {
          return this.getMessage(jala.Form.REQUIRE, "Please enter text into this field.");
       } else if (maxLength && reqData[this.name].length > maxLength) {
          return this.getMessage(jala.Form.MAXLENGTH, "Input for this field is too long ({0} characters). Please enter no more than {1} characters.",
                                     reqData[this.name].length, maxLength);
       } else if (minLength) {
          // set an error if the element is required but the input is too short
          // but don't throw an error if the element is optional and empty
          if (reqData[this.name].length < minLength &&
              (require || (!require && reqData[this.name].length > 0))) {
             return this.getMessage(jala.Form.MINLENGTH, "Input for this field is too short ({0} characters). Please enter at least {1} characters.",
                   reqData[this.name].length, minLength);
          }
       }
       return null;
    };

    /**
     * Checks user input against options set using the require method.
     * @param {Object} reqData request data
     * @returns String containing error message or null if everything is ok.
     * @type String
     * @see #checkLength
     * @see #require
     */
    jala.Form.Component.Input.prototype.checkRequirements = function(reqData) {
       return this.checkLength(reqData);
    };

    /**
     * Parses the string input from the form and creates the datatype that
     * is edited with this component. For the input component this method
     * is not of much use, but subclasses that edit other datatypes may use
     * it. For example, a date editor should convert the user input from string
     * to a date object.
     * @param {Object} reqData request data
     * @returns parsed value
     * @type Object
     */
    jala.Form.Component.Input.prototype.parseValue = function(reqData) {
       return reqData[this.name];
    };

    /**
     * Renders the html form elements to the response.
     * This method shall be overridden by subclasses of input component.
     * @param {Object} attr Basic attributes for the html form elements.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Input.prototype.renderControls = function(attr, value, reqData) {
       attr.value = (reqData) ? reqData[this.name] : value;
       if (this.getRequirement(jala.Form.MAXLENGTH)) {
          attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
       }
       jala.Form.html.input(attr);
       return;
    };

    /**
     * Constructs a newly created Password component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * password input tag.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Password component instance
     * @constructor
     */
    jala.Form.Component.Password = function Password(name) {
       jala.Form.Component.Password.superConstructor.apply(this, arguments);
       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Password, jala.Form.Component.Input);

    /**
     * Renders a password input tag to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Password.prototype.renderControls = function(attr, value, reqData) {
       attr.value = (reqData) ? reqData[this.name] : value;
       if (this.getRequirement(jala.Form.MAXLENGTH)) {
          attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
       }
       jala.Form.html.password(attr);
       return;
    };

    /**
     * Constructs a newly created Hidden component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * hidden input tag.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Hidden component instance
     * @constructor
     */
    jala.Form.Component.Hidden = function Hidden(name) {
       jala.Form.Component.Hidden.superConstructor.apply(this, arguments);
       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Hidden, jala.Form.Component.Input);

    /**
     * Renders this component directly to response. For a hidden tag, this is
     * just an input element, no div tag or anything.
     */
    jala.Form.Component.Hidden.prototype.render = function() {
       var attr = this.getControlAttributes();
       var tracker = this.form.getTracker()
       if (tracker) {
          this.renderControls(attr, null, tracker.reqData);
       } else {
          this.renderControls(attr, this.getValue());
       }
       return;
    };

    /**
     * Renders a hidden input tag to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Hidden.prototype.renderControls = function(attr, value, reqData) {
       attr.value = (reqData) ? reqData[this.name] : value;
       jala.Form.html.hidden(attr);
       return;
    };

    /**
     * Constructs a new Textarea component.
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * textarea input field.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Textarea component instance
     * @constructor
     */
    jala.Form.Component.Textarea = function Textarea(name) {
       jala.Form.Component.Textarea.superConstructor.apply(this, arguments);

       var rows, cols = undefined;

       /**
        * Returns the row numbers for this component.
        * @returns row numbers
        * @type String
        */
       this.getRows = function() {
          return rows;
       };

       /**
        * Sets the row numbers for this component.
        * @param {String} newRows new row numbers
        */
       this.setRows = function(newRows) {
          rows = newRows;
          return;
       };

       /**
        * Returns the col numbers for this component.
        * @returns col numbers
        * @type String
        */
       this.getCols = function() {
          return cols;
       };

       /**
        * Sets the col numbers for this component.
        * @param {String} newCols new col numbers
        */
       this.setCols = function(newCols) {
          cols = newCols;
          return;
       };

       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Textarea, jala.Form.Component.Input);

    /**
     * Renders a textarea input field to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Textarea.prototype.renderControls = function(attr, value, reqData) {
       attr.value = (reqData) ? reqData[this.name] : value;
       attr.rows = this.getRows() || 5;
       attr.cols = this.getCols() || 25;
       jala.Form.html.textArea(attr);
       return;
    };

    /**
     * Constructs a new Date component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * date editor.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Date component
     * @constructor
     */
    jala.Form.Component.Date = function Date(name) {
       jala.Form.Component.Date.superConstructor.apply(this, arguments);

       var dateFormat = "d.M.yyyy H:m";
       var dateFormatObj;

       /**
        * Returns the date format for this component.
        * @returns date format object
        * @type java.text.SimpleDateFormat
        */
       this.getDateFormat = function() {
          if (!dateFormatObj || dateFormatObj.toPattern() != dateFormat) {
             dateFormatObj = new java.text.SimpleDateFormat(dateFormat);
          }
          return dateFormatObj;
       };

       /**
        * Sets the date format for this component.
        * @param {String} newDateFormat new date format
        */
       this.setDateFormat = function(newDateFormat) {
          dateFormat = newDateFormat;
          return;
       };

       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Date, jala.Form.Component.Input);

    /**
     * Renders a textarea tag to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Date.prototype.renderControls = function(attr, value, reqData) {
       if (reqData) {
          attr.value = reqData[this.name];
       } else  if (value instanceof Date) {
          attr.value = this.getDateFormat().format(value);
       }
       if (this.getRequirement(jala.Form.MAXLENGTH)) {
          attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
       }
       jala.Form.html.input(attr);
       return;
    };

    /**
     * Validates user input from a date editor.
     * @param {Object} reqData request data
     * @returns null if everything is ok or string containing error message
     * @type String
     */
    jala.Form.Component.Date.prototype.checkRequirements = function(reqData) {
       try {
          this.parseValue(reqData);
          return null;
       } catch(e) {
          return this.getMessage("invalid", "This date cannot be parsed.");
       }
    };

    /**
     * Parses the string input from the form and converts it to a date object.
     * Throws an error if the string cannot be parsed.
     * @param {Object} reqData request data
     * @returns parsed date value
     * @type Date
     */
    jala.Form.Component.Date.prototype.parseValue = function(reqData) {
       return this.getDateFormat().parse(reqData[this.name]);
    };

    /**
     * Constructs a new Select component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * dropdown element.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Select component
     * @constructor
     */
    jala.Form.Component.Select = function Select(name) {
       jala.Form.Component.Select.superConstructor.apply(this, arguments);

       var options, firstOption = undefined;

       /**
        * Returns the option list for this component.
        */
       this.getOptions = function() {
          return options;
       };

       /**
        * Sets the option list for this component.
        * The argument may either be an array that will be used as option list,
        * or a function that is called when the option component is rendered and
        * has to return an option array.
        * For both arrays those formats are allowed:
        * <li>Array of arrays <code>[ [val, display], [val, display], .. ]</code></li>
        * <li>Array of objects <code>[ {value:val, display:display}, .. ]</code></li>
        * <li>Array of strings <code>[ display, display, .. ]</code> In this case,
        *    the index position of the string will be the value.</li>
        * @param {Array|Function} newOptions Array or function defining option list.
        */
       this.setOptions = function(newOptions) {
          options = newOptions;
          return;
       };

       /**
        * Returns the text that should be displayed if no value is selected.
        * @type String
        */
       this.getFirstOption = function() {
          return firstOption;
       };

       /**
        * Sets the text that is displayed if no value is selected
        * @param {String} newFirstOption text to display as first option element.
        */
       this.setFirstOption = function(newFirstOption) {
          firstOption = newFirstOption;
          return;
       };

       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Select, jala.Form.Component.Input);

    /**
     * Renders a dropdown element to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Select.prototype.renderControls = function(attr, value, reqData) {
       value = (reqData) ? reqData[this.name] : value;
       jala.Form.html.dropDown(attr, this.parseOptions(), value, this.getFirstOption());
       return;
    };

    /**
     * Validates user input from a dropdown element by making sure that
     * the option value list contains the user input.
     * @see jala.Form.Component.Select#checkOptions
     * @param {Object} reqData request data
     * @returns string containing error message or null if everything is ok.
     * @type String
     */
    jala.Form.Component.Select.prototype.checkRequirements = function(reqData) {
       return this.checkOptions(reqData);
    };

    /**
     * Creates an array of options for a dropdown element or a
     * group of radiobuttons. If options field of this element's
     * config is an array, that array is returned.
     * If options is a function, its return value is returned.
     * @returns array of options
     * @type Array
     */
    jala.Form.Component.Select.prototype.parseOptions = function() {
       var options = this.getOptions();
       if (options != null) {
          if (options instanceof Array) {
             return options;
          } else if (options instanceof Function) {
             return options.call(this.form.getDataObject(), this.name);
          }
       }
       return [];
    };

    /**
     * Checks user input for optiongroups: Unless require("checkoptions")
     * has ben set to false, the user input must exist in the option array.
     * @param {Object} reqData request data
     * @returns null if everything is ok or string containing error message
     * @type String
     */
    jala.Form.Component.Select.prototype.checkOptions = function(reqData) {
       // if field is required, an empty option is not allowed:
       var found = (!this.getRequirement(jala.Form.REQUIRE) && !reqData[this.name]);
       if (!found) {
          if (this.getRequirement(jala.Form.CHECKOPTIONS) === false) {
             // exit, if option check shall be suppressed
             return null;
          }
          var options = this.parseOptions();
          var val = reqData[this.name];
          for (var i=0; i<options.length; i++) {
             if ((options[i] instanceof Array) && options[i].length > 0) {
                // option is an array (1st element = value, 2nd = display)
                if (options[i][0] == reqData[this.name]) {
                   found = true;
                   break;
                }
             } else if (options[i].value && options[i].display) {
                // option is an object with fields value + display
                if (options[i].value == reqData[this.name]) {
                   found = true;
                   break;
                }
             } else {
                // option is a string, value is index number
                if (i == reqData[this.name]) {
                   found = true;
                   break;
                }
             }
          }
       }
       if (!found) {
          return "Please select a valid option.";
       }
       return null;
    };

    /**
     * Creates a new Radio component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * set of radio buttons.
     * @base jala.Form.Component.Select
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Radio component
     * @constructor
     */
    jala.Form.Component.Radio = function Radio(name) {
       jala.Form.Component.Radio.superConstructor.apply(this, arguments);
       return this;
    };
    // extend jala.Form.Component.Select
    jala.Form.extend(jala.Form.Component.Radio, jala.Form.Component.Select);

    /**
     * Renders a set of radio buttons to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     */
    jala.Form.Component.Radio.prototype.renderControls = function(attr, value) {
       var options = this.parseOptions();
       var optionAttr, optionDisplay;
       for (var i=0; i<options.length; i++) {
          optionAttr = attr.clone({}, false);
          optionAttr.id += i;
          if ((options[i] instanceof Array) && options[i].length > 0) {
             optionAttr.value = options[i][0];
             optionDisplay = options[i][1];
          } else if (options[i].value && options[i].display) {
             optionAttr.value = options[i].value;
             optionDisplay = options[i].display;
          } else {
             optionAttr.value = i;
             optionDisplay = options[i];
          }
          if (String(value) == String(optionAttr.value)) {
             optionAttr.checked = "checked";
          }
          jala.Form.html.radioButton(optionAttr);
          res.write(optionDisplay);
          jala.Form.html.tag("br");
       }
       return;
    };

    /**
     * Validates user input from a set of radio buttons and makes sure that
     * option value list contains the user input.
     * @see jala.Form.Component.Select#checkOptions
     * @param {Object} reqData request data
     * @returns null if everything is ok or string containing error message
     * @type String
     */
    jala.Form.Component.Radio.prototype.checkRequirements = function(reqData) {
       return this.checkOptions(reqData);
    };

    /**
     * Creates a new Checkbox component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * checkbox.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Checkbox component instance
     * @constructor
     */
    jala.Form.Component.Checkbox = function Checkbox(name) {
       jala.Form.Component.Checkbox.superConstructor.apply(this, arguments);
       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.Checkbox, jala.Form.Component.Input);

    /**
     * Renders an checkbox to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     */
    jala.Form.Component.Checkbox.prototype.renderControls = function(attr, value, reqData) {
       if (value == 1 || (reqData && reqData[this.name] == "1")) {
          attr.checked = "checked";
       }
       attr.value = "1";
       jala.Form.html.checkBox(attr);
       return;
    };

    /**
     * Parses the string input from the form. For a checked box, the value is 1,
     * for an unchecked box the value is 0.
     * @param {Object} reqData request data
     * @returns parsed value
     * @type Number
     */
    jala.Form.Component.Checkbox.prototype.parseValue = function(reqData) {
       return (reqData[this.name] == "1") ? 1 : 0;
    };

    /**
     * Validates user input from checkbox.
     * @param {Object} reqData request data
     * @returns null if everything is ok or string containing error message
     * @type String
     */
    jala.Form.Component.Checkbox.prototype.checkRequirements = function(reqData) {
       if (reqData[this.name] && reqData[this.name] != "1") {
          return this.getMessage("invalid", "The value of this checkbox is invalid.");
       }
       return null;
    };

    /**
     * Creates a new File component instance
     * @class Subclass of jala.Form.Component.Input which renders and validates a
     * file upload.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created File component
     * @constructor
     */
    jala.Form.Component.File = function File(name) {
       jala.Form.Component.File.superConstructor.apply(this, arguments);

       this.containsFileUpload = function() {
          return true;
       };

       return this;
    };
    // extend jala.Form.Component.Input
    jala.Form.extend(jala.Form.Component.File, jala.Form.Component.Input);

    /**
     * Renders a file input tag to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.File.prototype.renderControls = function(attr, value, reqData) {
       var contentType = this.getRequirement(jala.Form.CONTENTTYPE);
       if (contentType) {
          attr.accept = (contentType instanceof Array) ? contentType.join(",") : contentType;
       }
       jala.Form.html.file(attr);
       return;
    };

    /**
     * Validates a file upload by making sure it's there (if REQUIRE is set),
     * checking the file size, the content type and by trying to construct an image.
     * @param {Object} reqData request data
     * @param {jala.Form.Tracker} tracker jala.Form.Tracker object storing possible error messages
     * @returns null if everything is ok or string containing error message
     * @type String
     */
    jala.Form.Component.File.prototype.checkRequirements = function(reqData) {

       if (reqData[this.name].contentLength == 0) {
          // no upload
          if (this.getRequirement(jala.Form.REQUIRE) == true) {
             return this.getMessage(jala.Form.REQUIRE, "File upload is required.");
          } else {
             // no further checks necessary, exit here
             return null;
          }
       }

       var maxLength = this.getRequirement(jala.Form.MAXLENGTH);
       if (maxLength && reqData[this.name].contentLength > maxLength) {
          return this.getMessage(jala.Form.MAXLENGTH, "This file is too big ({0} bytes), maximum allowed size {1} bytes.",
                reqData[this.name].contentLength, maxLength);
       }

       var contentType = this.getRequirement(jala.Form.CONTENTTYPE);
       if (contentType) {
          var arr = (contentType instanceof Array) ? contentType : [contentType];
          if (arr.indexOf(reqData[this.name].contentType) == -1) {
             return this.getMessage(jala.Form.CONTENTTYPE, "The file type {0} is not allowed.",
                reqData[this.name].contentType);
          }
       }

       return null;
    };

    /**
     * Creates a new Image component instance
     * @class Subclass of jala.Form.Component.File which renders a file upload
     * and validates uploaded files as images.
     * @base jala.Form.Component.File
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Image component
     * @constructor
     */
    // FIXME: see below
    jala.Form.Component.Image = function() {};

    /**
     * FIXME: JSDoc has some sever problems with this class.
     * It's somehow due to the named method ("Image") that it
     * always appears as global static object.
     * Wrapping the method in another function which immediately
     * is executed seems to solve this problem and could be used
     * as a work-around for similar issues.
     */
    jala.Form.Component.Image = (function() {
       return function Image(name) {
          jala.Form.Component.Image.superConstructor.apply(this, arguments);
          return this;
       };
    })();

    // extend jala.Form.Component.File
    jala.Form.extend(jala.Form.Component.Image, jala.Form.Component.File);

    /**
     * Validates an image upload by making sure it's there (if REQUIRE is set),
     * checking the file size, the content type and by trying to construct an image.
     * If the file is an image, width and height limitations set by require are
     * checked.
     * @param {Object} reqData request data
     * @type String
     */
    jala.Form.Component.Image.prototype.checkRequirements = function(reqData) {
       var re = this.constructor.superConstructor.prototype.checkRequirements.call(this, reqData);
       if (re) {
          return re;
       }

       if (reqData[this.name].contentLength > 0) {
          var helmaImg = undefined;
          try {
             helmaImg = new Image(reqData[this.name]);
          } catch (imgError) {
             return this.getMessage("invalid", "This image file can't be processed.");
          }

          var maxWidth = this.getRequirement(jala.Form.MAXWIDTH);
          if (maxWidth && helmaImg.getWidth() > maxWidth) {
             return this.getMessage("maxwidth", "This image is too wide.");
          }

          var minWidth = this.getRequirement(jala.Form.MINWIDTH);
          if (minWidth && helmaImg.getWidth() < minWidth) {
             return this.getMessage("minwidth", "This image is not wide enough.");
          }

          var maxHeight = this.getRequirement(jala.Form.MAXHEIGHT);
          if (maxHeight && helmaImg.getHeight() > maxHeight) {
             return this.getMessage("maxheight", "This image is too tall.");
          }

          var minHeight = this.getRequirement(jala.Form.MINHEIGHT);
          if (minHeight && helmaImg.getHeight() < minHeight) {
             return this.getMessage("minheight", "This image is not tall enough.");
          }
       }

       return null;
    };

    /**
     * Creates a new Button component instance
     * @class Subclass of jala.Form.Component.Input which renders a button.
     * @base jala.Form.Component.Input
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Button component
     * @constructor
     */
    jala.Form.Component.Button = function Button(name) {
       jala.Form.Component.Button.superConstructor.apply(this, arguments);

       /**
        * Private field containing the value of the button (ie. the visible text)
        * @type String
        */
       var value;

       /**
        * Returns the value set for this button.
        * @returns value
        * @type String
        */
       this.getValue = function() {
          return value;
       };

       /**
        * Sets the value for this button.
        * @param {String} newValue new value
        */
       this.setValue = function(newValue) {
          value = newValue;
          return;
       };

       return this;
    };
    // extend jala.Form.Component
    jala.Form.extend(jala.Form.Component.Button, jala.Form.Component.Input);

    /**
     * Renders a button to the response.
     * @param {Object} attr Basic attributes for this element.
     * @param {Object} value Value to be used for rendering this element.
     * @param {Object} reqData Request data for the whole form. This argument is
     *       passed only if the form is re-rendered after an error occured.
     */
    jala.Form.Component.Button.prototype.render = function(attr, value, reqData) {
       var classStr = (this.getClassName()) ? " " + this.getClassName() : "";
       var attr = {
          id: this.createDomId(),
          "class": "component" + classStr
       };
       jala.Form.html.openTag("div", attr);
       res.write("\n ");

       this.renderControls(this.getControlAttributes(), this.getValue());
       res.write("\n");

       jala.Form.html.closeTag("div");
       res.write("\n");
       return;
    };

    /**
     * Creates a new attribute object for this button.
     * @returns Object with all attributes set for this button.
     * @type Object
     */
    jala.Form.Component.Button.prototype.renderControls = function(attr, value) {
       if (value) {
          attr.value = value;
       }
       jala.Form.html.button(attr);
       return;
    };

    /**
     * Creates a new Submit component instance
     * @class Subclass of jala.Form.Component.Button which renders a submit button.
     * @base jala.Form.Component.Button
     * @param {String} name Name of the component, used as name of the html controls.
     * @returns A newly created Submit component
     * @constructor
     */
    jala.Form.Component.Submit = function Submit(name) {
       jala.Form.Component.Submit.superConstructor.apply(this, arguments);

       return this;
    };
    // extend jala.Form.Component.Button
    jala.Form.extend(jala.Form.Component.Submit, jala.Form.Component.Button);

    /**
     * Creates a new attribute object for this button.
     * @returns Object with all attributes set for this button.
     * @type Object
     */
    jala.Form.Component.Submit.prototype.renderControls = function(attr, value) {
       if (value) {
          attr.value = value;
       }
       jala.Form.html.submit(attr);
       return;
    };

    /**
     * static default getter function used to return a field
     * from the data object.
     * @param {String} name Name of the property.
     * @type Object
     */
    jala.Form.propertyGetter = function(name, value) {
       return this[name];
    };

    /**
     * static default setter function used to change a field
     * of the data object.
     * @param {String} name Name of the property.
     * @param {Object} value New value of the property.
     */
    jala.Form.propertySetter = function(name, value) {
       this[name] = value;
    };

    /**
     * A generic container for error-messages and values
     * @class Instances of this class can contain error-messages and values
     * @constructor
     * @type jala.Form.Tracker
     */
    jala.Form.Tracker = function(reqData) {

       /**
        * A map containing input from request data
        * @type Object
        */
       this.reqData = reqData;

       /**
        * A map containing parsed values (only for those fields that didn't
        * fail during checkRequirements method).
        * @type Object
        */
       this.values = {};

       /**
        * A map containing error messages
        * @type Object
        */
       this.errors = {};

       return this;
    };

    /** @ignore */
    jala.Form.Tracker.toString = function() {
       return "[jala.Form.Tracker]";
    };

    /** @ignore */
    jala.Form.Tracker.prototype.toString = jala.Form.Tracker.toString;

    /**
     * Returns true if an error has been set for at least one component.
     * @returns true if form encountered an error.
     * @type Boolean
     */
    jala.Form.Tracker.prototype.hasError = function() {
       for (var keys in this.errors) {
          return true;
       }
       return false;
    };

    /**
     * Returns the number of components for which this instance has
     * tracked an error.
     * @returns Number of components that did not validate.
     * @type Number
     */
    jala.Form.Tracker.prototype.countErrors = function() {
       var ct = 0;
       for (var keys in this.errors) {
          ct++;
       }
       return ct;
    };

    /**
     * Helper method.
     * @private
     */
    jala.Form.Tracker.prototype.debug = function() {
       for (var key in this.errors) {
          res.debug(key + ":" + this.errors[key]);
       }
       return;
    };