You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
14 KiB
JavaScript
432 lines
14 KiB
JavaScript
dojo.provide("dijit.form._FormWidget");
|
|
|
|
dojo.require("dijit._Widget");
|
|
dojo.require("dijit._Templated");
|
|
|
|
dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated],
|
|
{
|
|
//
|
|
// 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.
|
|
|
|
// baseClass: [protected] String
|
|
// Root CSS class of the widget (ex: dijitTextBox), used to add CSS classes of widget
|
|
// (ex: "dijitTextBox dijitTextBoxInvalid dijitTextBoxFocused dijitTextBoxInvalidFocused")
|
|
// See _setStateClass().
|
|
baseClass: "",
|
|
|
|
// name: 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,
|
|
|
|
// 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,
|
|
|
|
// 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",
|
|
disabled: "focusNode",
|
|
readOnly: "focusNode",
|
|
id: "focusNode",
|
|
tabIndex: "focusNode",
|
|
alt: "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
|
|
this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
_setDisabledAttr: function(/*Boolean*/ value){
|
|
this.disabled = value;
|
|
dojo.attr(this.focusNode, 'disabled', value);
|
|
dijit.setWaiState(this.focusNode, "disabled", value);
|
|
|
|
if(value){
|
|
//reset those, because after the domNode is disabled, we can no longer receive
|
|
//mouse related events, see #4200
|
|
this._hovering = false;
|
|
this._active = false;
|
|
// remove the tabIndex, especially for FF
|
|
this.focusNode.removeAttribute('tabIndex');
|
|
}else{
|
|
this.focusNode.setAttribute('tabIndex', this.tabIndex);
|
|
}
|
|
this._setStateClass();
|
|
},
|
|
|
|
setDisabled: function(/*Boolean*/ disabled){
|
|
// summary:
|
|
// Deprecated. Use attr('disabled', ...) instead.
|
|
dojo.deprecated("setDisabled("+disabled+") is deprecated. Use attr('disabled',"+disabled+") instead.", "", "2.0");
|
|
this.attr('disabled', disabled);
|
|
},
|
|
|
|
_onFocus: function(e){
|
|
if(this.scrollOnFocus){
|
|
dijit.scrollIntoView(this.domNode);
|
|
}
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
_onMouse : function(/*Event*/ event){
|
|
// summary:
|
|
// Sets _hovering, _active, and stateModifier properties depending on mouse state,
|
|
// then calls setStateClass() to set appropriate CSS classes for this.domNode.
|
|
//
|
|
// To get a different CSS class for hover, send onmouseover and onmouseout events to this method.
|
|
// To get a different CSS class while mouse button is depressed, send onmousedown to this method.
|
|
|
|
var mouseNode = event.currentTarget;
|
|
if(mouseNode && mouseNode.getAttribute){
|
|
this.stateModifier = mouseNode.getAttribute("stateModifier") || "";
|
|
}
|
|
|
|
if(!this.disabled){
|
|
switch(event.type){
|
|
case "mouseenter":
|
|
case "mouseover":
|
|
this._hovering = true;
|
|
this._active = this._mouseDown;
|
|
break;
|
|
|
|
case "mouseout":
|
|
case "mouseleave":
|
|
this._hovering = false;
|
|
this._active = false;
|
|
break;
|
|
|
|
case "mousedown" :
|
|
this._active = true;
|
|
this._mouseDown = true;
|
|
// set a global event to handle mouseup, so it fires properly
|
|
// even if the cursor leaves the button
|
|
var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
|
|
//if user clicks on the button, even if the mouse is released outside of it,
|
|
//this button should get focus (which mimics native browser buttons)
|
|
if(this._mouseDown && this.isFocusable()){
|
|
this.focus();
|
|
}
|
|
this._active = false;
|
|
this._mouseDown = false;
|
|
this._setStateClass();
|
|
this.disconnect(mouseUpConnector);
|
|
});
|
|
break;
|
|
}
|
|
this._setStateClass();
|
|
}
|
|
},
|
|
|
|
isFocusable: function(){
|
|
// summary:
|
|
// Tells if this widget is focusable or not. Used internally by dijit.
|
|
// tags:
|
|
// protected
|
|
return !this.disabled && !this.readOnly && this.focusNode && (dojo.style(this.domNode, "display") != "none");
|
|
},
|
|
|
|
focus: function(){
|
|
// summary:
|
|
// Put focus on this widget
|
|
dijit.focus(this.focusNode);
|
|
},
|
|
|
|
_setStateClass: function(){
|
|
// summary:
|
|
// Update the visual state of the widget by setting the css classes on this.domNode
|
|
// (or this.stateNode if defined) by combining this.baseClass with
|
|
// various suffixes that represent the current widget state(s).
|
|
//
|
|
// description:
|
|
// In the case where a widget has multiple
|
|
// states, it sets the class based on all possible
|
|
// combinations. For example, an invalid form widget that is being hovered
|
|
// will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
|
|
//
|
|
// For complex widgets with multiple regions, there can be various hover/active states,
|
|
// such as "Hover" or "CloseButtonHover" (for tab buttons).
|
|
// This is controlled by a stateModifier="CloseButton" attribute on the close button node.
|
|
//
|
|
// The widget may have one or more of the following states, determined
|
|
// by this.state, this.checked, this.valid, and this.selected:
|
|
// - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
|
|
// - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
|
|
// - Selected - ex: currently selected tab will have this.selected==true
|
|
//
|
|
// In addition, it may have one or more of the following states,
|
|
// based on this.disabled and flags set in _onMouse (this._active, this._hovering, this._focused):
|
|
// - Disabled - if the widget is disabled
|
|
// - Active - if the mouse (or space/enter key?) is being pressed down
|
|
// - Focused - if the widget has focus
|
|
// - Hover - if the mouse is over the widget
|
|
|
|
// Compute new set of classes
|
|
var newStateClasses = this.baseClass.split(" ");
|
|
|
|
function multiply(modifier){
|
|
newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
|
|
}
|
|
|
|
if(this.checked){
|
|
multiply("Checked");
|
|
}
|
|
if(this.state){
|
|
multiply(this.state);
|
|
}
|
|
if(this.selected){
|
|
multiply("Selected");
|
|
}
|
|
|
|
if(this.disabled){
|
|
multiply("Disabled");
|
|
}else if(this.readOnly){
|
|
multiply("ReadOnly");
|
|
}else if(this._active){
|
|
multiply(this.stateModifier+"Active");
|
|
}else{
|
|
if(this._focused){
|
|
multiply("Focused");
|
|
}
|
|
if(this._hovering){
|
|
multiply(this.stateModifier+"Hover");
|
|
}
|
|
}
|
|
|
|
// Remove old state classes and add new ones.
|
|
// For performance concerns we only write into domNode.className once.
|
|
var tn = this.stateNode || this.domNode,
|
|
classHash = {}; // set of all classes (state and otherwise) for node
|
|
|
|
dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
|
|
|
|
if("_stateClasses" in this){
|
|
dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
|
|
}
|
|
|
|
dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
|
|
|
|
var newClasses = [];
|
|
for(var c in classHash){
|
|
newClasses.push(c);
|
|
}
|
|
tn.className = newClasses.join(" ");
|
|
|
|
this._stateClasses = newStateClasses;
|
|
},
|
|
|
|
compare: function(/*anything*/val1, /*anything*/val2){
|
|
// summary:
|
|
// Compare 2 values (as returned by attr('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==true,
|
|
// onChange is only called form priorityChange=true events.
|
|
// tags:
|
|
// private
|
|
this._lastValue = newValue;
|
|
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;
|
|
}
|
|
if((this.intermediateChanges || priorityChange || priorityChange === undefined) &&
|
|
((typeof newValue != typeof this._lastValueReported) ||
|
|
this.compare(newValue, this._lastValueReported) != 0)){
|
|
this._lastValueReported = newValue;
|
|
if(this._onChangeActive){ this.onChange(newValue); }
|
|
}
|
|
},
|
|
|
|
create: function(){
|
|
// Overrides _Widget.create()
|
|
this.inherited(arguments);
|
|
this._onChangeActive = true;
|
|
this._setStateClass();
|
|
},
|
|
|
|
destroy: function(){
|
|
if(this._layoutHackHandle){
|
|
clearTimeout(this._layoutHackHandle);
|
|
}
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
setValue: function(/*String*/ value){
|
|
// summary:
|
|
// Deprecated. Use attr('value', ...) instead.
|
|
dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use attr('value',"+value+") instead.", "", "2.0");
|
|
this.attr('value', value);
|
|
},
|
|
|
|
getValue: function(){
|
|
// summary:
|
|
// Deprecated. Use attr('value') instead.
|
|
dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use attr('value') instead.", "", "2.0");
|
|
return this.attr('value');
|
|
},
|
|
|
|
_layoutHack: function(){
|
|
// summary:
|
|
// Work around table sizing bugs on FF2 by forcing redraw
|
|
|
|
if(dojo.isFF == 2 && !this._layoutHackHandle){
|
|
var node=this.domNode;
|
|
var old = node.style.opacity;
|
|
node.style.opacity = "0.999";
|
|
this._layoutHackHandle = setTimeout(dojo.hitch(this, function(){
|
|
this._layoutHackHandle = null;
|
|
node.style.opacity = old;
|
|
}), 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)
|
|
attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { value: "" }),
|
|
|
|
postCreate: function(){
|
|
if(dojo.isIE || dojo.isWebKit){ // IE won't stop the event with keypress and Safari won't send an ESCAPE to keypress at all
|
|
this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
|
|
}
|
|
// Update our reset value if it hasn't yet been set (because this.attr
|
|
// is only called when there *is* a value
|
|
if(this._resetValue === undefined){
|
|
this._resetValue = this.value;
|
|
}
|
|
},
|
|
|
|
_setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
|
|
// summary:
|
|
// Hook so attr('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.value = newValue;
|
|
this._handleOnChange(newValue, priorityChange);
|
|
},
|
|
|
|
_getValueAttr: function(/*String*/ value){
|
|
// summary:
|
|
// Hook so attr('value') works.
|
|
return this._lastValue;
|
|
},
|
|
|
|
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){
|
|
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);
|
|
}else if(dojo.isWebKit){ // ESCAPE needs help making it into keypress
|
|
te = document.createEvent('Events');
|
|
te.initEvent('keypress', true, true);
|
|
te.keyCode = dojo.keys.ESCAPE;
|
|
te.shiftKey = e.shiftKey;
|
|
e.target.dispatchEvent(te);
|
|
}
|
|
}
|
|
}
|
|
});
|