385 lines
11 KiB
JavaScript
385 lines
11 KiB
JavaScript
|
if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
||
|
dojo._hasResource["dijit.form.Form"] = true;
|
||
|
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.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 a JSON structure
|
||
|
|
||
|
// 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);
|
||
|
});
|
||
|
|
||
|
// call setValue() or setAttribute('checked') for each widget, according to obj
|
||
|
for(var name in map){
|
||
|
var widgets = map[name], // array of widgets w/this name
|
||
|
values = dojo.getObject(name, false, obj); // list of values for those widgets
|
||
|
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: generate JSON structure from form values
|
||
|
|
||
|
// get widget values
|
||
|
var obj = { };
|
||
|
dojo.forEach(this.getDescendants(), function(widget){
|
||
|
var name = widget.name;
|
||
|
if(!name){ 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.isValid || widget.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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
|
||
|
}
|