summaryrefslogtreecommitdiff
path: root/lib/dijit/form/_FormWidget.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/form/_FormWidget.js')
-rw-r--r--lib/dijit/form/_FormWidget.js515
1 files changed, 363 insertions, 152 deletions
diff --git a/lib/dijit/form/_FormWidget.js b/lib/dijit/form/_FormWidget.js
index eb80ca06b..ebd1cfb18 100644
--- a/lib/dijit/form/_FormWidget.js
+++ b/lib/dijit/form/_FormWidget.js
@@ -1,166 +1,377 @@
/*
- 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._FormWidget"]){
-dojo._hasResource["dijit.form._FormWidget"]=true;
+if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit.form._FormWidget"] = true;
dojo.provide("dijit.form._FormWidget");
dojo.require("dojo.window");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit._CssStateMixin");
-dojo.declare("dijit.form._FormWidget",[dijit._Widget,dijit._Templated,dijit._CssStateMixin],{name:"",alt:"",value:"",type:"text",tabIndex:"0",disabled:false,intermediateChanges:false,scrollOnFocus:true,attributeMap:dojo.delegate(dijit._Widget.prototype.attributeMap,{value:"focusNode",id:"focusNode",tabIndex:"focusNode",alt:"focusNode",title:"focusNode"}),postMixInProperties:function(){
-this.nameAttrSetting=this.name?("name=\""+this.name.replace(/'/g,""")+"\""):"";
-this.inherited(arguments);
-},postCreate:function(){
-this.inherited(arguments);
-this.connect(this.domNode,"onmousedown","_onMouseDown");
-},_setDisabledAttr:function(_1){
-this.disabled=_1;
-dojo.attr(this.focusNode,"disabled",_1);
-if(this.valueNode){
-dojo.attr(this.valueNode,"disabled",_1);
-}
-dijit.setWaiState(this.focusNode,"disabled",_1);
-if(_1){
-this._hovering=false;
-this._active=false;
-var _2="tabIndex" in this.attributeMap?this.attributeMap.tabIndex:"focusNode";
-dojo.forEach(dojo.isArray(_2)?_2:[_2],function(_3){
-var _4=this[_3];
-if(dojo.isWebKit||dijit.hasDefaultTabStop(_4)){
-_4.setAttribute("tabIndex","-1");
-}else{
-_4.removeAttribute("tabIndex");
-}
-},this);
-}else{
-this.focusNode.setAttribute("tabIndex",this.tabIndex);
-}
-},setDisabled:function(_5){
-dojo.deprecated("setDisabled("+_5+") is deprecated. Use set('disabled',"+_5+") instead.","","2.0");
-this.set("disabled",_5);
-},_onFocus:function(e){
-if(this.scrollOnFocus){
-dojo.window.scrollIntoView(this.domNode);
-}
-this.inherited(arguments);
-},isFocusable:function(){
-return !this.disabled&&!this.readOnly&&this.focusNode&&(dojo.style(this.domNode,"display")!="none");
-},focus:function(){
-dijit.focus(this.focusNode);
-},compare:function(_6,_7){
-if(typeof _6=="number"&&typeof _7=="number"){
-return (isNaN(_6)&&isNaN(_7))?0:_6-_7;
-}else{
-if(_6>_7){
-return 1;
-}else{
-if(_6<_7){
-return -1;
-}else{
-return 0;
-}
-}
-}
-},onChange:function(_8){
-},_onChangeActive:false,_handleOnChange:function(_9,_a){
-this._lastValue=_9;
-if(this._lastValueReported==undefined&&(_a===null||!this._onChangeActive)){
-this._resetValue=this._lastValueReported=_9;
-}
-if((this.intermediateChanges||_a||_a===undefined)&&((typeof _9!=typeof this._lastValueReported)||this.compare(_9,this._lastValueReported)!=0)){
-this._lastValueReported=_9;
-if(this._onChangeActive){
-if(this._onChangeHandle){
-clearTimeout(this._onChangeHandle);
-}
-this._onChangeHandle=setTimeout(dojo.hitch(this,function(){
-this._onChangeHandle=null;
-this.onChange(_9);
-}),0);
-}
-}
-},create:function(){
-this.inherited(arguments);
-this._onChangeActive=true;
-},destroy:function(){
-if(this._onChangeHandle){
-clearTimeout(this._onChangeHandle);
-this.onChange(this._lastValueReported);
-}
-this.inherited(arguments);
-},setValue:function(_b){
-dojo.deprecated("dijit.form._FormWidget:setValue("+_b+") is deprecated. Use set('value',"+_b+") instead.","","2.0");
-this.set("value",_b);
-},getValue:function(){
-dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.","","2.0");
-return this.get("value");
-},_onMouseDown:function(e){
-if(!e.ctrlKey&&this.isFocusable()){
-var _c=this.connect(dojo.body(),"onmouseup",function(){
-if(this.isFocusable()){
-this.focus();
-}
-this.disconnect(_c);
+
+
+dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
+ {
+ // summary:
+ // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
+ // which can be children of a <form> node or a `dijit.form.Form` widget.
+ //
+ // description:
+ // Represents a single HTML element.
+ // All these widgets should have these attributes just like native HTML input elements.
+ // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
+ //
+ // They also share some common methods.
+
+ // name: [const] String
+ // Name used when submitting form; same as "name" attribute or plain HTML elements
+ name: "",
+
+ // alt: String
+ // Corresponds to the native HTML <input> element's attribute.
+ alt: "",
+
+ // value: String
+ // Corresponds to the native HTML <input> element's attribute.
+ value: "",
+
+ // type: String
+ // Corresponds to the native HTML <input> element's attribute.
+ type: "text",
+
+ // tabIndex: Integer
+ // Order fields are traversed when user hits the tab key
+ tabIndex: "0",
+
+ // disabled: Boolean
+ // Should this widget respond to user input?
+ // In markup, this is specified as "disabled='disabled'", or just "disabled".
+ disabled: false,
+
+ // intermediateChanges: Boolean
+ // Fires onChange for each value change or only on demand
+ intermediateChanges: false,
+
+ // scrollOnFocus: Boolean
+ // On focus, should this widget scroll into view?
+ scrollOnFocus: true,
+
+ // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
+ attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
+ value: "focusNode",
+ id: "focusNode",
+ tabIndex: "focusNode",
+ alt: "focusNode",
+ title: "focusNode"
+ }),
+
+ postMixInProperties: function(){
+ // Setup name=foo string to be referenced from the template (but only if a name has been specified)
+ // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
+ // Regarding escaping, see heading "Attribute values" in
+ // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
+ this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
+ this.inherited(arguments);
+ },
+
+ postCreate: function(){
+ this.inherited(arguments);
+ this.connect(this.domNode, "onmousedown", "_onMouseDown");
+ },
+
+ _setDisabledAttr: function(/*Boolean*/ value){
+ this._set("disabled", value);
+ dojo.attr(this.focusNode, 'disabled', value);
+ if(this.valueNode){
+ dojo.attr(this.valueNode, 'disabled', value);
+ }
+ dijit.setWaiState(this.focusNode, "disabled", value);
+
+ if(value){
+ // reset these, because after the domNode is disabled, we can no longer receive
+ // mouse related events, see #4200
+ this._set("hovering", false);
+ this._set("active", false);
+
+ // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
+ var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
+ dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
+ var node = this[attachPointName];
+ // complex code because tabIndex=-1 on a <div> doesn't work on FF
+ if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug
+ node.setAttribute('tabIndex', "-1");
+ }else{
+ node.removeAttribute('tabIndex');
+ }
+ }, this);
+ }else{
+ if(this.tabIndex != ""){
+ this.focusNode.setAttribute('tabIndex', this.tabIndex);
+ }
+ }
+ },
+
+ setDisabled: function(/*Boolean*/ disabled){
+ // summary:
+ // Deprecated. Use set('disabled', ...) instead.
+ dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
+ this.set('disabled', disabled);
+ },
+
+ _onFocus: function(e){
+ if(this.scrollOnFocus){
+ dojo.window.scrollIntoView(this.domNode);
+ }
+ this.inherited(arguments);
+ },
+
+ isFocusable: function(){
+ // summary:
+ // Tells if this widget is focusable or not. Used internally by dijit.
+ // tags:
+ // protected
+ return !this.disabled && this.focusNode && (dojo.style(this.domNode, "display") != "none");
+ },
+
+ focus: function(){
+ // summary:
+ // Put focus on this widget
+ if(!this.disabled){
+ dijit.focus(this.focusNode);
+ }
+ },
+
+ compare: function(/*anything*/ val1, /*anything*/ val2){
+ // summary:
+ // Compare 2 values (as returned by get('value') for this widget).
+ // tags:
+ // protected
+ if(typeof val1 == "number" && typeof val2 == "number"){
+ return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
+ }else if(val1 > val2){
+ return 1;
+ }else if(val1 < val2){
+ return -1;
+ }else{
+ return 0;
+ }
+ },
+
+ onChange: function(newValue){
+ // summary:
+ // Callback when this widget's value is changed.
+ // tags:
+ // callback
+ },
+
+ // _onChangeActive: [private] Boolean
+ // Indicates that changes to the value should call onChange() callback.
+ // This is false during widget initialization, to avoid calling onChange()
+ // when the initial value is set.
+ _onChangeActive: false,
+
+ _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
+ // summary:
+ // Called when the value of the widget is set. Calls onChange() if appropriate
+ // newValue:
+ // the new value
+ // priorityChange:
+ // For a slider, for example, dragging the slider is priorityChange==false,
+ // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
+ // onChange is only called form priorityChange=true events.
+ // tags:
+ // private
+ if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
+ // this block executes not for a change, but during initialization,
+ // and is used to store away the original value (or for ToggleButton, the original checked state)
+ this._resetValue = this._lastValueReported = newValue;
+ }
+ this._pendingOnChange = this._pendingOnChange
+ || (typeof newValue != typeof this._lastValueReported)
+ || (this.compare(newValue, this._lastValueReported) != 0);
+ if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
+ this._lastValueReported = newValue;
+ this._pendingOnChange = false;
+ if(this._onChangeActive){
+ if(this._onChangeHandle){
+ clearTimeout(this._onChangeHandle);
+ }
+ // setTimout allows hidden value processing to run and
+ // also the onChange handler can safely adjust focus, etc
+ this._onChangeHandle = setTimeout(dojo.hitch(this,
+ function(){
+ this._onChangeHandle = null;
+ this.onChange(newValue);
+ }), 0); // try to collapse multiple onChange's fired faster than can be processed
+ }
+ }
+ },
+
+ create: function(){
+ // Overrides _Widget.create()
+ this.inherited(arguments);
+ this._onChangeActive = true;
+ },
+
+ destroy: function(){
+ if(this._onChangeHandle){ // destroy called before last onChange has fired
+ clearTimeout(this._onChangeHandle);
+ this.onChange(this._lastValueReported);
+ }
+ this.inherited(arguments);
+ },
+
+ setValue: function(/*String*/ value){
+ // summary:
+ // Deprecated. Use set('value', ...) instead.
+ dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
+ this.set('value', value);
+ },
+
+ getValue: function(){
+ // summary:
+ // Deprecated. Use get('value') instead.
+ dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
+ return this.get('value');
+ },
+
+ _onMouseDown: function(e){
+ // If user clicks on the button, even if the mouse is released outside of it,
+ // this button should get focus (to mimics native browser buttons).
+ // This is also needed on chrome because otherwise buttons won't get focus at all,
+ // which leads to bizarre focus restore on Dialog close etc.
+ if(!e.ctrlKey && dojo.mouseButtons.isLeft(e) && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
+ // Set a global event to handle mouseup, so it fires properly
+ // even if the cursor leaves this.domNode before the mouse up event.
+ var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
+ if (this.isFocusable()) {
+ this.focus();
+ }
+ this.disconnect(mouseUpConnector);
+ });
+ }
+ }
});
-}
-}});
-dojo.declare("dijit.form._FormValueWidget",dijit.form._FormWidget,{readOnly:false,attributeMap:dojo.delegate(dijit.form._FormWidget.prototype.attributeMap,{value:"",readOnly:"focusNode"}),_setReadOnlyAttr:function(_d){
-this.readOnly=_d;
-dojo.attr(this.focusNode,"readOnly",_d);
-dijit.setWaiState(this.focusNode,"readonly",_d);
-},postCreate:function(){
-this.inherited(arguments);
-if(dojo.isIE){
-this.connect(this.focusNode||this.domNode,"onkeydown",this._onKeyDown);
-}
-if(this._resetValue===undefined){
-this._resetValue=this.value;
-}
-},_setValueAttr:function(_e,_f){
-this.value=_e;
-this._handleOnChange(_e,_f);
-},_getValueAttr:function(){
-return this._lastValue;
-},undo:function(){
-this._setValueAttr(this._lastValueReported,false);
-},reset:function(){
-this._hasBeenBlurred=false;
-this._setValueAttr(this._resetValue,true);
-},_onKeyDown:function(e){
-if(e.keyCode==dojo.keys.ESCAPE&&!(e.ctrlKey||e.altKey||e.metaKey)){
-var te;
-if(dojo.isIE){
-e.preventDefault();
-te=document.createEventObject();
-te.keyCode=dojo.keys.ESCAPE;
-te.shiftKey=e.shiftKey;
-e.srcElement.fireEvent("onkeypress",te);
-}
-}
-},_layoutHackIE7:function(){
-if(dojo.isIE==7){
-var _10=this.domNode;
-var _11=_10.parentNode;
-var _12=_10.firstChild||_10;
-var _13=_12.style.filter;
-var _14=this;
-while(_11&&_11.clientHeight==0){
-(function ping(){
-var _15=_14.connect(_11,"onscroll",function(e){
-_14.disconnect(_15);
-_12.style.filter=(new Date()).getMilliseconds();
-setTimeout(function(){
-_12.style.filter=_13;
-},0);
+
+dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
+{
+ // summary:
+ // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
+ // description:
+ // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
+ // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
+ // works as expected.
+
+ // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
+ // directly in the template as read by the parser in order to function. IE is known to specifically
+ // require the 'name' attribute at element creation time. See #8484, #8660.
+ // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
+ // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
+ // Seems like we really want value removed from attributeMap altogether
+ // (although there's no easy way to do that now)
+
+ // readOnly: Boolean
+ // Should this widget respond to user input?
+ // In markup, this is specified as "readOnly".
+ // Similar to disabled except readOnly form values are submitted.
+ readOnly: false,
+
+ attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
+ value: "",
+ readOnly: "focusNode"
+ }),
+
+ _setReadOnlyAttr: function(/*Boolean*/ value){
+ dojo.attr(this.focusNode, 'readOnly', value);
+ dijit.setWaiState(this.focusNode, "readonly", value);
+ this._set("readOnly", value);
+ },
+
+ postCreate: function(){
+ this.inherited(arguments);
+
+ if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ // IE won't stop the event with keypress
+ this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
+ }
+ // Update our reset value if it hasn't yet been set (because this.set()
+ // is only called when there *is* a value)
+ if(this._resetValue === undefined){
+ this._lastValueReported = this._resetValue = this.value;
+ }
+ },
+
+ _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
+ // summary:
+ // Hook so set('value', value) works.
+ // description:
+ // Sets the value of the widget.
+ // If the value has changed, then fire onChange event, unless priorityChange
+ // is specified as null (or false?)
+ this._handleOnChange(newValue, priorityChange);
+ },
+
+ _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
+ // summary:
+ // Called when the value of the widget has changed. Saves the new value in this.value,
+ // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
+ this._set("value", newValue);
+ this.inherited(arguments);
+ },
+
+ undo: function(){
+ // summary:
+ // Restore the value to the last value passed to onChange
+ this._setValueAttr(this._lastValueReported, false);
+ },
+
+ reset: function(){
+ // summary:
+ // Reset the widget's value to what it was at initialization time
+ this._hasBeenBlurred = false;
+ this._setValueAttr(this._resetValue, true);
+ },
+
+ _onKeyDown: function(e){
+ if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
+ var te;
+ if(dojo.isIE){
+ e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
+ te = document.createEventObject();
+ te.keyCode = dojo.keys.ESCAPE;
+ te.shiftKey = e.shiftKey;
+ e.srcElement.fireEvent('onkeypress', te);
+ }
+ }
+ },
+
+ _layoutHackIE7: function(){
+ // summary:
+ // Work around table sizing bugs on IE7 by forcing redraw
+
+ if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
+ var domNode = this.domNode;
+ var parent = domNode.parentNode;
+ var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
+ var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
+ var _this = this;
+ while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
+ (function ping(){
+ var disconnectHandle = _this.connect(parent, "onscroll",
+ function(e){
+ _this.disconnect(disconnectHandle); // only call once
+ pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
+ setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
+ }
+ );
+ })();
+ parent = parent.parentNode;
+ }
+ }
+ }
});
-})();
-_11=_11.parentNode;
-}
-}
-}});
+
}