- Author:
- David Nickerson <nickerso@users.sourceforge.net>
- Date:
- 2009-07-16 02:00:03+12:00
- Desc:
- the starting point for the HH tutorial example
- Permanent Source URI:
- https://models.fieldml.org/workspace/a1/rawfile/f6a8f90307388eb4b040ee3566b84d88b59247f7/dojo-presentation/js/dojo/dijit/form/Form.js
dojo.provide("dijit.form.Form");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.declare("dijit.form._FormMixin", null,
{
//
// summary:
// Widget corresponding to HTML form tag, for validation and serialization
//
// example:
// | <form dojoType="dijit.form.Form" id="myForm">
// | Name: <input type="text" name="name" />
// | </form>
// | myObj = {name: "John Doe"};
// | dijit.byId('myForm').setValues(myObj);
// |
// | myObj=dijit.byId('myForm').getValues();
// TODO:
// * Repeater
// * better handling for arrays. Often form elements have names with [] like
// * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
//
//
reset: function(){
dojo.forEach(this.getDescendants(), function(widget){
if(widget.reset){
widget.reset();
}
});
},
validate: function(){
// summary: returns if the form is valid - same as isValid - but
// provides a few additional (ui-specific) features.
// 1 - it will highlight any sub-widgets that are not
// valid
// 2 - it will call focus() on the first invalid
// sub-widget
var didFocus = false;
return dojo.every(dojo.map(this.getDescendants(), function(widget){
// Need to set this so that "required" widgets get their
// state set.
widget._hasBeenBlurred = true;
var valid = widget.disabled || !widget.validate || widget.validate();
if (!valid && !didFocus) {
// Set focus of the first non-valid widget
dijit.scrollIntoView(widget.containerNode||widget.domNode);
widget.focus();
didFocus = true;
}
return valid;
}), "return item;");
},
setValues: function(/*object*/obj){
// summary: Fill in form values from according to an Object (in the format returned by getValues())
// generate map from name --> [list of widgets with that name]
var map = { };
dojo.forEach(this.getDescendants(), function(widget){
if(!widget.name){ return; }
var entry = map[widget.name] || (map[widget.name] = [] );
entry.push(widget);
});
for(var name in map){
if(!map.hasOwnProperty(name)){
continue;
}
var widgets = map[name], // array of widgets w/this name
values = dojo.getObject(name, false, obj); // list of values for those widgets
if(values===undefined){
continue;
}
if(!dojo.isArray(values)){
values = [ values ];
}
if(typeof widgets[0].checked == 'boolean'){
// for checkbox/radio, values is a list of which widgets should be checked
dojo.forEach(widgets, function(w, i){
w.setValue(dojo.indexOf(values, w.value) != -1);
});
}else if(widgets[0]._multiValue){
// it takes an array (e.g. multi-select)
widgets[0].setValue(values);
}else{
// otherwise, values is a list of values to be assigned sequentially to each widget
dojo.forEach(widgets, function(w, i){
w.setValue(values[i]);
});
}
}
/***
* TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
dojo.forEach(this.containerNode.elements, function(element){
if (element.name == ''){return}; // like "continue"
var namePath = element.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var p=namePath[j - 1];
// repeater support block
var nameA=p.split("[");
if (nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
myObj=myObj[nameA[0]][nameIndex];
continue;
} // repeater support ends
if(typeof(myObj[p]) == "undefined"){
myObj=undefined;
break;
};
myObj=myObj[p];
}
if (typeof(myObj) == "undefined"){
return; // like "continue"
}
if (typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
return; // like "continue"
}
// TODO: widget values (just call setValue() on the widget)
switch(element.type){
case "checkbox":
element.checked = (name in myObj) &&
dojo.some(myObj[name], function(val){ return val==element.value; });
break;
case "radio":
element.checked = (name in myObj) && myObj[name]==element.value;
break;
case "select-multiple":
element.selectedIndex=-1;
dojo.forEach(element.options, function(option){
option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
});
break;
case "select-one":
element.selectedIndex="0";
dojo.forEach(element.options, function(option){
option.selected = option.value == myObj[name];
});
break;
case "hidden":
case "text":
case "textarea":
case "password":
element.value = myObj[name] || "";
break;
}
});
*/
},
getValues: function(){
// summary:
// Returns Object representing form values.
// description:
// Returns name/value hash for each form element.
// If there are multiple elements w/the same name, value is an array,
// unless they are radio buttons in which case value is a scalar since only
// one can be checked at a time.
//
// If the name is a dot separated list (like a.b.c.d), creates a nested structure.
// Only works on widget form elements.
// example:
// | { name: "John Smith", interests: ["sports", "movies"] }
// get widget values
var obj = { };
dojo.forEach(this.getDescendants(), function(widget){
var name = widget.name;
if(!name||widget.disabled){ return; }
// Single value widget (checkbox, radio, or plain <input> type widget
var value = (widget.getValue && !widget._getValueDeprecated) ? widget.getValue() : widget.value;
// Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
if(typeof widget.checked == 'boolean'){
if(/Radio/.test(widget.declaredClass)){
// radio button
if(value !== false){
dojo.setObject(name, value, obj);
}
}else{
// checkbox/toggle button
var ary=dojo.getObject(name, false, obj);
if(!ary){
ary=[];
dojo.setObject(name, ary, obj);
}
if(value !== false){
ary.push(value);
}
}
}else{
// plain input
dojo.setObject(name, value, obj);
}
});
/***
* code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
* but it doesn't understand [] notation, presumably)
var obj = { };
dojo.forEach(this.containerNode.elements, function(elm){
if (!elm.name) {
return; // like "continue"
}
var namePath = elm.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var nameIndex = null;
var p=namePath[j - 1];
var nameA=p.split("[");
if (nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
} else if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]] = { }
} // if
if (nameA.length == 1){
myObj=myObj[nameA[0]];
} else{
myObj=myObj[nameA[0]][nameIndex];
} // if
} // for
if ((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type=="radio" && elm.checked)){
if(name == name.split("[")[0]){
myObj[name]=elm.value;
} else{
// can not set value when there is no name
}
} else if (elm.type == "checkbox" && elm.checked){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
myObj[name].push(elm.value);
} else if (elm.type == "select-multiple"){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
for (var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
if (elm.options[jdx].selected){
myObj[name].push(elm.options[jdx].value);
}
}
} // if
name=undefined;
}); // forEach
***/
return obj;
},
// TODO: ComboBox might need time to process a recently input value. This should be async?
isValid: function(){
// summary: make sure that every widget that has a validator function returns true
return dojo.every(this.getDescendants(), function(widget){
return widget.disabled || !widget.isValid || widget.isValid();
});
},
onValidStateChange: function(isValid){
// summary: stub function to connect to if you want to do something
// (like disable/enable a submit button) when the valid
// state changes on the form as a whole.
},
_widgetChange: function(){
// summary: connected to a widgets onChange function - update our
// valid state, if needed.
var isValid = this.isValid();
if (isValid !== this._lastValidState){
this._lastValidState = isValid;
this.onValidStateChange(isValid);
}
},
connectChildren: function(){
// summary: connects to the onChange function of all children to
// track valid state changes. You can call this function
// directly, ie. in the event that you programmatically
// add a widget to the form *after* the form has been
// initialized
dojo.forEach(this._changeConnections, dojo.hitch(this, "disconnect"));
var _this = this;
// we connect to validate - so that it better reflects the states
// of the widgets - also, we only connect if it has a validate
// function (to avoid too many unneeded connections)
this._changeConnections = dojo.map(
dojo.filter(this.getDescendants(),
function(item){ return item.validate; }
),
function(widget){
return _this.connect(widget, "validate", "_widgetChange");
}
);
// Call the widget change function to update the valid state, in
// case something is different now.
this._widgetChange();
},
startup: function(){
this.inherited(arguments);
// Initialize our valid state tracking. Needs to be done in startup
// because it's not guaranteed that our children are initialized
// yet.
this._changeConnections = [];
this.connectChildren();
this._lastValidState = this.isValid();
}
});
dojo.declare(
"dijit.form.Form",
[dijit._Widget, dijit._Templated, dijit.form._FormMixin],
{
// summary:
// Adds conveniences to regular HTML form
// HTML <FORM> attributes
name: "",
action: "",
method: "",
encType: "",
"accept-charset": "",
accept: "",
target: "",
templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' name='${name}'></form>",
attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
{action: "", method: "", encType: "", "accept-charset": "", accept: "", target: ""}),
execute: function(/*Object*/ formContents){
// summary:
// Deprecated: use submit()
},
onExecute: function(){
// summary:
// Deprecated: use onSubmit()
},
setAttribute: function(/*String*/ attr, /*anything*/ value){
this.inherited(arguments);
switch(attr){
case "encType":
if(dojo.isIE){ this.domNode.encoding = value; }
}
},
postCreate: function(){
// IE tries to hide encType
if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
var item = this.srcNodeRef.attributes.getNamedItem('encType');
if(item && !item.specified && (typeof item.value == "string")){
this.setAttribute('encType', item.value);
}
}
this.inherited(arguments);
},
onReset: function(/*Event?*/e){
// summary:
// Callback when user resets the form. This method is intended
// to be over-ridden. When the `reset` method is called
// programmatically, the return value from `onReset` is used
// to compute whether or not resetting should proceed
return true; // Boolean
},
_onReset: function(e){
// create fake event so we can know if preventDefault() is called
var faux = {
returnValue: true, // the IE way
preventDefault: function(){ // not IE
this.returnValue = false;
},
stopPropagation: function(){}, currentTarget: e.currentTarget, target: e.target
};
// if return value is not exactly false, and haven't called preventDefault(), then reset
if(!(this.onReset(faux) === false) && faux.returnValue){
this.reset();
}
dojo.stopEvent(e);
return false;
},
_onSubmit: function(e){
var fp = dijit.form.Form.prototype;
// TODO: remove ths if statement beginning with 2.0
if(this.execute != fp.execute || this.onExecute != fp.onExecute){
dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
this.onExecute();
this.execute(this.getValues());
}
if(this.onSubmit(e) === false){ // only exactly false stops submit
dojo.stopEvent(e);
}
},
onSubmit: function(/*Event?*/e){
// summary:
// Callback when user submits the form. This method is
// intended to be over-ridden, but by default it checks and
// returns the validity of form elements. When the `submit`
// method is called programmatically, the return value from
// `onSubmit` is used to compute whether or not submission
// should proceed
return this.isValid(); // Boolean
},
submit: function(){
// summary:
// programmatically submit form if and only if the `onSubmit` returns true
if(!(this.onSubmit() === false)){
this.containerNode.submit();
}
}
}
);