From 81bea17aefb26859f825b9293c7c99192874806e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 8 Nov 2011 20:40:44 +0400 Subject: upgrade Dojo to 1.6.1 --- lib/dijit/form/_FormMixin.js | 601 ++++++++++++++++++++++++++++++++----------- 1 file changed, 452 insertions(+), 149 deletions(-) (limited to 'lib/dijit/form/_FormMixin.js') diff --git a/lib/dijit/form/_FormMixin.js b/lib/dijit/form/_FormMixin.js index 4c52ed9e2..e42702d31 100644 --- a/lib/dijit/form/_FormMixin.js +++ b/lib/dijit/form/_FormMixin.js @@ -1,158 +1,461 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit.form._FormMixin"]){ -dojo._hasResource["dijit.form._FormMixin"]=true; +if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form._FormMixin"] = true; dojo.provide("dijit.form._FormMixin"); dojo.require("dojo.window"); -dojo.declare("dijit.form._FormMixin",null,{reset:function(){ -dojo.forEach(this.getDescendants(),function(_1){ -if(_1.reset){ -_1.reset(); -} -}); -},validate:function(){ -var _2=false; -return dojo.every(dojo.map(this.getDescendants(),function(_3){ -_3._hasBeenBlurred=true; -var _4=_3.disabled||!_3.validate||_3.validate(); -if(!_4&&!_2){ -dojo.window.scrollIntoView(_3.containerNode||_3.domNode); -_3.focus(); -_2=true; -} -return _4; -}),function(_5){ -return _5; -}); -},setValues:function(_6){ -dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.","","2.0"); -return this.set("value",_6); -},_setValueAttr:function(_7){ -var _8={}; -dojo.forEach(this.getDescendants(),function(_9){ -if(!_9.name){ -return; -} -var _a=_8[_9.name]||(_8[_9.name]=[]); -_a.push(_9); -}); -for(var _b in _8){ -if(!_8.hasOwnProperty(_b)){ -continue; -} -var _c=_8[_b],_d=dojo.getObject(_b,false,_7); -if(_d===undefined){ -continue; -} -if(!dojo.isArray(_d)){ -_d=[_d]; -} -if(typeof _c[0].checked=="boolean"){ -dojo.forEach(_c,function(w,i){ -w.set("value",dojo.indexOf(_d,w.value)!=-1); -}); -}else{ -if(_c[0].multiple){ -_c[0].set("value",_d); -}else{ -dojo.forEach(_c,function(w,i){ -w.set("value",_d[i]); -}); -} -} -} -},getValues:function(){ -dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.","","2.0"); -return this.get("value"); -},_getValueAttr:function(){ -var _e={}; -dojo.forEach(this.getDescendants(),function(_f){ -var _10=_f.name; -if(!_10||_f.disabled){ -return; -} -var _11=_f.get("value"); -if(typeof _f.checked=="boolean"){ -if(/Radio/.test(_f.declaredClass)){ -if(_11!==false){ -dojo.setObject(_10,_11,_e); -}else{ -_11=dojo.getObject(_10,false,_e); -if(_11===undefined){ -dojo.setObject(_10,null,_e); -} -} -}else{ -var ary=dojo.getObject(_10,false,_e); -if(!ary){ -ary=[]; -dojo.setObject(_10,ary,_e); -} -if(_11!==false){ -ary.push(_11); -} -} -}else{ -var _12=dojo.getObject(_10,false,_e); -if(typeof _12!="undefined"){ -if(dojo.isArray(_12)){ -_12.push(_11); -}else{ -dojo.setObject(_10,[_12,_11],_e); -} -}else{ -dojo.setObject(_10,_11,_e); -} -} -}); -return _e; -},isValid:function(){ -this._invalidWidgets=dojo.filter(this.getDescendants(),function(_13){ -return !_13.disabled&&_13.isValid&&!_13.isValid(); -}); -return !this._invalidWidgets.length; -},onValidStateChange:function(_14){ -},_widgetChange:function(_15){ -var _16=this._lastValidState; -if(!_15||this._lastValidState===undefined){ -_16=this.isValid(); -if(this._lastValidState===undefined){ -this._lastValidState=_16; -} -}else{ -if(_15.isValid){ -this._invalidWidgets=dojo.filter(this._invalidWidgets||[],function(w){ -return (w!=_15); -},this); -if(!_15.isValid()&&!_15.get("disabled")){ -this._invalidWidgets.push(_15); -} -_16=(this._invalidWidgets.length===0); -} -} -if(_16!==this._lastValidState){ -this._lastValidState=_16; -this.onValidStateChange(_16); -} -},connectChildren:function(){ -dojo.forEach(this._changeConnections,dojo.hitch(this,"disconnect")); -var _17=this; -var _18=(this._changeConnections=[]); -dojo.forEach(dojo.filter(this.getDescendants(),function(_19){ -return _19.validate; -}),function(_1a){ -_18.push(_17.connect(_1a,"validate",dojo.hitch(_17,"_widgetChange",_1a))); -_18.push(_17.connect(_1a,"_setDisabledAttr",dojo.hitch(_17,"_widgetChange",_1a))); -}); -this._widgetChange(null); -},startup:function(){ -this.inherited(arguments); -this._changeConnections=[]; -this.connectChildren(); -}}); + + +dojo.declare("dijit.form._FormMixin", null, { + // summary: + // Mixin for containers of form widgets (i.e. widgets that represent a single value + // and can be children of a
node or dijit.form.Form widget) + // description: + // Can extract all the form widgets + // values and combine them into a single javascript object, or alternately + // take such an object and set the values for all the contained + // form widgets + +/*===== + // value: Object + // Name/value hash for each child widget with a name and value. + // Child widgets without names are not part of the hash. + // + // If there are multiple child widgets w/the same name, value is an array, + // unless they are radio buttons in which case value is a scalar (since only + // one radio button can be checked at a time). + // + // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure. + // + // Example: + // | { name: "John Smith", interests: ["sports", "movies"] } +=====*/ + + // state: [readonly] String + // Will be "Error" if one or more of the child widgets has an invalid value, + // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "", + // which indicates that the form is ready to be submitted. + state: "", + + // 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 + dojo.window.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 set('value', val) instead.", "", "2.0"); + return this.set('value', val); + }, + _setValueAttr: function(/*Object*/ obj){ + // summary: + // Fill in form values from according to an Object (in the format returned by get('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.set('value', dojo.indexOf(values, w.value) != -1); + }); + }else if(widgets[0].multiple){ + // it takes an array (e.g. multi-select) + widgets[0].set('value', values); + }else{ + // otherwise, values is a list of values to be assigned sequentially to each widget + dojo.forEach(widgets, function(w, i){ + w.set('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 set('value', ...) on the widget) + + // TODO: maybe should call dojo.getNodeProp() instead + 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; + } + }); + */ + + // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events + // which I am monitoring. + }, + + getValues: function(){ + dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0"); + return this.get('value'); + }, + _getValueAttr: function(){ + // summary: + // Returns Object representing form values. See description of `value` for details. + // description: + + // The value is updated into this.value every time a child has an onChange event, + // so in the common case this function could just return this.value. However, + // that wouldn't work when: + // + // 1. User presses return key to submit a form. That doesn't fire an onchange event, + // and even if it did it would come too late due to the setTimout(..., 0) in _handleOnChange() + // + // 2. app for some reason calls this.get("value") while the user is typing into a + // form field. Not sure if that case needs to be supported or not. + + // 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.get('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{ + // give radio widgets a default of null + value = dojo.getObject(name, false, obj); + if(value === undefined){ + dojo.setObject(name, null, 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{ + var prev=dojo.getObject(name, false, obj); + if(typeof prev != "undefined"){ + if(dojo.isArray(prev)){ + prev.push(value); + }else{ + dojo.setObject(name, [prev, value], obj); + } + }else{ + // unique name + 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= 0 ? "Error" : + dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : ""; + }, + + disconnectChildren: function(){ + // summary: + // Remove connections to monitor changes to children's value, error state, and disabled state, + // in order to update Form.value and Form.state. + dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect")); + dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); }); + }, + + connectChildren: function(/*Boolean*/ inStartup){ + // summary: + // Setup connections to monitor changes to children's value, error state, and disabled state, + // in order to update Form.value and Form.state. + // + // You can call this function directly, ex. in the event that you + // programmatically add a widget to the form *after* the form has been + // initialized. + + var _this = this; + + // Remove old connections, if any + this.disconnectChildren(); + + this._descendants = this.getDescendants(); + + // (Re)set this.value and this.state. Send watch() notifications but not on startup. + var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set"); + set("value", this.get("value")); + set("state", this._getState()); + + // Monitor changes to error state and disabled state in order to update + // Form.state + var conns = (this._childConnections = []), + watches = (this._childWatches = []); + dojo.forEach(dojo.filter(this._descendants, + function(item){ return item.validate; } + ), + function(widget){ + // We are interested in whenever the widget changes validity state - or + // whenever the disabled attribute on that widget is changed. + dojo.forEach(["state", "disabled"], function(attr){ + watches.push(widget.watch(attr, function(attr, oldVal, newVal){ + _this.set("state", _this._getState()); + })); + }); + }); + + // And monitor calls to child.onChange so we can update this.value + var onChange = function(){ + // summary: + // Called when child's value or disabled state changes + + // Use setTimeout() to collapse value changes in multiple children into a single + // update to my value. Multiple updates will occur on: + // 1. Form.set() + // 2. Form.reset() + // 3. user selecting a radio button (which will de-select another radio button, + // causing two onChange events) + if(_this._onChangeDelayTimer){ + clearTimeout(_this._onChangeDelayTimer); + } + _this._onChangeDelayTimer = setTimeout(function(){ + delete _this._onChangeDelayTimer; + _this._set("value", _this.get("value")); + }, 10); + }; + dojo.forEach( + dojo.filter(this._descendants, function(item){ return item.onChange; } ), + function(widget){ + // When a child widget's value changes, + // the efficient thing to do is to just update that one attribute in this.value, + // but that gets a little complicated when a checkbox is checked/unchecked + // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name. + // Doing simple thing for now. + conns.push(_this.connect(widget, "onChange", onChange)); + + // Disabling/enabling a child widget should remove it's value from this.value. + // Again, this code could be more efficient, doing simple thing for now. + watches.push(widget.watch("disabled", onChange)); + } + ); + }, + + startup: function(){ + this.inherited(arguments); + + // Initialize value and valid/invalid state tracking. Needs to be done in startup() + // so that children are initialized. + this.connectChildren(true); + + // Make state change call onValidStateChange(), will be removed in 2.0 + this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); }); + }, + + destroy: function(){ + this.disconnectChildren(); + this.inherited(arguments); + } + + }); + } -- cgit v1.2.3