2523 lines
73 KiB
JavaScript
2523 lines
73 KiB
JavaScript
//
|
|
// 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;
|
|
};
|
|
|
|
|