summaryrefslogtreecommitdiff
path: root/lib/dijit/_CssStateMixin.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/_CssStateMixin.js')
-rw-r--r--lib/dijit/_CssStateMixin.js395
1 files changed, 255 insertions, 140 deletions
diff --git a/lib/dijit/_CssStateMixin.js b/lib/dijit/_CssStateMixin.js
index 53144804f..2fdecdcdf 100644
--- a/lib/dijit/_CssStateMixin.js
+++ b/lib/dijit/_CssStateMixin.js
@@ -1,149 +1,264 @@
/*
- 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._CssStateMixin"]){
-dojo._hasResource["dijit._CssStateMixin"]=true;
+if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit._CssStateMixin"] = true;
dojo.provide("dijit._CssStateMixin");
-dojo.declare("dijit._CssStateMixin",[],{cssStateNodes:{},postCreate:function(){
-this.inherited(arguments);
-dojo.forEach(["onmouseenter","onmouseleave","onmousedown"],function(e){
-this.connect(this.domNode,e,"_cssMouseEvent");
-},this);
-this.connect(this,"set",function(_1,_2){
-if(arguments.length>=2&&{disabled:true,readOnly:true,checked:true,selected:true}[_1]){
-this._setStateClass();
-}
-});
-dojo.forEach(["_onFocus","_onBlur"],function(ap){
-this.connect(this,ap,"_setStateClass");
-},this);
-for(var ap in this.cssStateNodes){
-this._trackMouseState(this[ap],this.cssStateNodes[ap]);
-}
-this._setStateClass();
-},_cssMouseEvent:function(_3){
-if(!this.disabled){
-switch(_3.type){
-case "mouseenter":
-case "mouseover":
-this._hovering=true;
-this._active=this._mouseDown;
-break;
-case "mouseleave":
-case "mouseout":
-this._hovering=false;
-this._active=false;
-break;
-case "mousedown":
-this._active=true;
-this._mouseDown=true;
-var _4=this.connect(dojo.body(),"onmouseup",function(){
-this._active=false;
-this._mouseDown=false;
-this._setStateClass();
-this.disconnect(_4);
-});
-break;
-}
-this._setStateClass();
-}
-},_setStateClass:function(){
-var _5=this.baseClass.split(" ");
-function _6(_7){
-_5=_5.concat(dojo.map(_5,function(c){
-return c+_7;
-}),"dijit"+_7);
-};
-if(!this.isLeftToRight()){
-_6("Rtl");
-}
-if(this.checked){
-_6("Checked");
-}
-if(this.state){
-_6(this.state);
-}
-if(this.selected){
-_6("Selected");
-}
-if(this.disabled){
-_6("Disabled");
-}else{
-if(this.readOnly){
-_6("ReadOnly");
-}else{
-if(this._active){
-_6("Active");
-}else{
-if(this._hovering){
-_6("Hover");
-}
-}
-}
-}
-if(this._focused){
-_6("Focused");
-}
-var tn=this.stateNode||this.domNode,_8={};
-dojo.forEach(tn.className.split(" "),function(c){
-_8[c]=true;
-});
-if("_stateClasses" in this){
-dojo.forEach(this._stateClasses,function(c){
-delete _8[c];
-});
-}
-dojo.forEach(_5,function(c){
-_8[c]=true;
-});
-var _9=[];
-for(var c in _8){
-_9.push(c);
-}
-tn.className=_9.join(" ");
-this._stateClasses=_5;
-},_trackMouseState:function(_a,_b){
-var _c=false,_d=false,_e=false;
-var _f=this,cn=dojo.hitch(this,"connect",_a);
-function _10(){
-var _11=("disabled" in _f&&_f.disabled)||("readonly" in _f&&_f.readonly);
-dojo.toggleClass(_a,_b+"Hover",_c&&!_d&&!_11);
-dojo.toggleClass(_a,_b+"Active",_d&&!_11);
-dojo.toggleClass(_a,_b+"Focused",_e&&!_11);
-};
-cn("onmouseenter",function(){
-_c=true;
-_10();
-});
-cn("onmouseleave",function(){
-_c=false;
-_d=false;
-_10();
-});
-cn("onmousedown",function(){
-_d=true;
-_10();
-});
-cn("onmouseup",function(){
-_d=false;
-_10();
-});
-cn("onfocus",function(){
-_e=true;
-_10();
-});
-cn("onblur",function(){
-_e=false;
-_10();
-});
-this.connect(this,"set",function(_12,_13){
-if(_12=="disabled"||_12=="readOnly"){
-_10();
-}
+
+
+dojo.declare("dijit._CssStateMixin", [], {
+ // summary:
+ // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
+ // state changes, and also higher-level state changes such becoming disabled or selected.
+ //
+ // description:
+ // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
+ // maintain CSS classes on the widget root node (this.domNode) depending on hover,
+ // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
+ // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
+ //
+ // It also sets CSS like dijitButtonDisabled based on widget semantic state.
+ //
+ // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
+ // within the widget).
+
+ // cssStateNodes: [protected] Object
+ // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
+ //.
+ // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
+ // (like "dijitUpArrowButton"). Example:
+ // | {
+ // | "upArrowButton": "dijitUpArrowButton",
+ // | "downArrowButton": "dijitDownArrowButton"
+ // | }
+ // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
+ // is hovered, etc.
+ cssStateNodes: {},
+
+ // hovering: [readonly] Boolean
+ // True if cursor is over this widget
+ hovering: false,
+
+ // active: [readonly] Boolean
+ // True if mouse was pressed while over this widget, and hasn't been released yet
+ active: false,
+
+ _applyAttributes: function(){
+ // This code would typically be in postCreate(), but putting in _applyAttributes() for
+ // performance: so the class changes happen before DOM is inserted into the document.
+ // Change back to postCreate() in 2.0. See #11635.
+
+ this.inherited(arguments);
+
+ // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
+ dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
+ this.connect(this.domNode, e, "_cssMouseEvent");
+ }, this);
+
+ // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
+ dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
+ this.watch(attr, dojo.hitch(this, "_setStateClass"));
+ }, this);
+
+ // Events on sub nodes within the widget
+ for(var ap in this.cssStateNodes){
+ this._trackMouseState(this[ap], this.cssStateNodes[ap]);
+ }
+ // Set state initially; there's probably no hover/active/focus state but widget might be
+ // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
+ this._setStateClass();
+ },
+
+ _cssMouseEvent: function(/*Event*/ event){
+ // summary:
+ // Sets hovering and active properties depending on mouse state,
+ // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
+
+ if(!this.disabled){
+ switch(event.type){
+ case "mouseenter":
+ case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
+ this._set("hovering", true);
+ this._set("active", this._mouseDown);
+ break;
+
+ case "mouseleave":
+ case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
+ this._set("hovering", false);
+ this._set("active", false);
+ break;
+
+ case "mousedown" :
+ this._set("active", true);
+ this._mouseDown = true;
+ // Set a global event to handle mouseup, so it fires properly
+ // even if the cursor leaves this.domNode before the mouse up event.
+ // Alternately could set active=false on mouseout.
+ var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
+ this._mouseDown = false;
+ this._set("active", false);
+ this.disconnect(mouseUpConnector);
+ });
+ break;
+ }
+ }
+ },
+
+ _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".
+ //
+ // 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
+ // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
+ // - 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) and from focus manager (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.isLeftToRight()){
+ // For RTL mode we need to set an addition class like dijitTextBoxRtl.
+ multiply("Rtl");
+ }
+
+ 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("Active");
+ }else if(this.hovering){
+ multiply("Hover");
+ }
+ }
+
+ if(this._focused){
+ multiply("Focused");
+ }
+
+ // 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;
+ },
+
+ _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
+ // summary:
+ // Track mouse/focus events on specified node and set CSS class on that node to indicate
+ // current state. Usually not called directly, but via cssStateNodes attribute.
+ // description:
+ // Given class=foo, will set the following CSS class on the node
+ // - fooActive: if the user is currently pressing down the mouse button while over the node
+ // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
+ // - fooFocus: if the node is focused
+ //
+ // Note that it won't set any classes if the widget is disabled.
+ // node: DomNode
+ // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
+ // is handled specially and automatically just by mixing in this class.
+ // clazz: String
+ // CSS class name (ex: dijitSliderUpArrow).
+
+ // Current state of node (initially false)
+ // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
+ var hovering=false, active=false, focused=false;
+
+ var self = this,
+ cn = dojo.hitch(this, "connect", node);
+
+ function setClass(){
+ var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
+ dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
+ dojo.toggleClass(node, clazz+"Active", active && !disabled);
+ dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
+ }
+
+ // Mouse
+ cn("onmouseenter", function(){
+ hovering = true;
+ setClass();
+ });
+ cn("onmouseleave", function(){
+ hovering = false;
+ active = false;
+ setClass();
+ });
+ cn("onmousedown", function(){
+ active = true;
+ setClass();
+ });
+ cn("onmouseup", function(){
+ active = false;
+ setClass();
+ });
+
+ // Focus
+ cn("onfocus", function(){
+ focused = true;
+ setClass();
+ });
+ cn("onblur", function(){
+ focused = false;
+ setClass();
+ });
+
+ // Just in case widget is enabled/disabled while it has focus/hover/active state.
+ // Maybe this is overkill.
+ this.watch("disabled", setClass);
+ this.watch("readOnly", setClass);
+ }
});
-}});
+
}