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: // |
// | Name: // |
// | myObj = {name: "John Doe"}; // | dijit.byId('myForm').attr('value', myObj); // | // | myObj=dijit.byId('myForm').attr('value'); /*===== // value: Object // 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), it's a nested structure. // Only works on widget form elements. // example: // | { name: "John Smith", interests: ["sports", "movies"] } =====*/ // 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; }), function(item) { return item; }); }, setValues: function(val){ dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use attr('value', val) instead.", "", "2.0"); return this.attr('value', val); }, _setValueAttr: function(/*object*/obj){ // summary: Fill in form values from according to an Object (in the format returned by attr('value')) // 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.attr('value', dojo.indexOf(values, w.value) != -1); }); }else if(widgets[0]._multiValue){ // it takes an array (e.g. multi-select) widgets[0].attr('value', values); }else{ // otherwise, values is a list of values to be assigned sequentially to each widget dojo.forEach(widgets, function(w, i){ w.attr('value', 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 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 attr('value', ...) 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(){ dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use attr('value') instead.", "", "2.0"); return this.attr('value'); }, _getValueAttr: 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 type widget var value = widget.attr('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 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 attributes name: "", action: "", method: "", encType: "", "accept-charset": "", accept: "", target: "", templateString: "
", 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() }, _setEncTypeAttr: function(/*String*/ value){ this.encType = value; dojo.attr(this.domNode, "encType", value); if(dojo.isIE){ this.domNode.encoding = value; } }, postCreate: function(){ // IE tries to hide encType // TODO: this code should be in parser, not here. if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){ var item = this.srcNodeRef.attributes.getNamedItem('encType'); if(item && !item.specified && (typeof item.value == "string")){ this.attr('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(); } } } );