]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/_CssStateMixin.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / _CssStateMixin.js
1 /*
2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5 */
6
7
8 if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo._hasResource["dijit._CssStateMixin"] = true;
10 dojo.provide("dijit._CssStateMixin");
11
12
13 dojo.declare("dijit._CssStateMixin", [], {
14 // summary:
15 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
16 // state changes, and also higher-level state changes such becoming disabled or selected.
17 //
18 // description:
19 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
20 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
21 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
22 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
23 //
24 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
25 //
26 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
27 // within the widget).
28
29 // cssStateNodes: [protected] Object
30 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
31 //.
32 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
33 // (like "dijitUpArrowButton"). Example:
34 // | {
35 // | "upArrowButton": "dijitUpArrowButton",
36 // | "downArrowButton": "dijitDownArrowButton"
37 // | }
38 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
39 // is hovered, etc.
40 cssStateNodes: {},
41
42 // hovering: [readonly] Boolean
43 // True if cursor is over this widget
44 hovering: false,
45
46 // active: [readonly] Boolean
47 // True if mouse was pressed while over this widget, and hasn't been released yet
48 active: false,
49
50 _applyAttributes: function(){
51 // This code would typically be in postCreate(), but putting in _applyAttributes() for
52 // performance: so the class changes happen before DOM is inserted into the document.
53 // Change back to postCreate() in 2.0. See #11635.
54
55 this.inherited(arguments);
56
57 // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
58 dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
59 this.connect(this.domNode, e, "_cssMouseEvent");
60 }, this);
61
62 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
63 dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
64 this.watch(attr, dojo.hitch(this, "_setStateClass"));
65 }, this);
66
67 // Events on sub nodes within the widget
68 for(var ap in this.cssStateNodes){
69 this._trackMouseState(this[ap], this.cssStateNodes[ap]);
70 }
71 // Set state initially; there's probably no hover/active/focus state but widget might be
72 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
73 this._setStateClass();
74 },
75
76 _cssMouseEvent: function(/*Event*/ event){
77 // summary:
78 // Sets hovering and active properties depending on mouse state,
79 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
80
81 if(!this.disabled){
82 switch(event.type){
83 case "mouseenter":
84 case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
85 this._set("hovering", true);
86 this._set("active", this._mouseDown);
87 break;
88
89 case "mouseleave":
90 case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
91 this._set("hovering", false);
92 this._set("active", false);
93 break;
94
95 case "mousedown" :
96 this._set("active", true);
97 this._mouseDown = true;
98 // Set a global event to handle mouseup, so it fires properly
99 // even if the cursor leaves this.domNode before the mouse up event.
100 // Alternately could set active=false on mouseout.
101 var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
102 this._mouseDown = false;
103 this._set("active", false);
104 this.disconnect(mouseUpConnector);
105 });
106 break;
107 }
108 }
109 },
110
111 _setStateClass: function(){
112 // summary:
113 // Update the visual state of the widget by setting the css classes on this.domNode
114 // (or this.stateNode if defined) by combining this.baseClass with
115 // various suffixes that represent the current widget state(s).
116 //
117 // description:
118 // In the case where a widget has multiple
119 // states, it sets the class based on all possible
120 // combinations. For example, an invalid form widget that is being hovered
121 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
122 //
123 // The widget may have one or more of the following states, determined
124 // by this.state, this.checked, this.valid, and this.selected:
125 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
126 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
127 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
128 // - Selected - ex: currently selected tab will have this.selected==true
129 //
130 // In addition, it may have one or more of the following states,
131 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
132 // - Disabled - if the widget is disabled
133 // - Active - if the mouse (or space/enter key?) is being pressed down
134 // - Focused - if the widget has focus
135 // - Hover - if the mouse is over the widget
136
137 // Compute new set of classes
138 var newStateClasses = this.baseClass.split(" ");
139
140 function multiply(modifier){
141 newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
142 }
143
144 if(!this.isLeftToRight()){
145 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
146 multiply("Rtl");
147 }
148
149 if(this.checked){
150 multiply("Checked");
151 }
152 if(this.state){
153 multiply(this.state);
154 }
155 if(this.selected){
156 multiply("Selected");
157 }
158
159 if(this.disabled){
160 multiply("Disabled");
161 }else if(this.readOnly){
162 multiply("ReadOnly");
163 }else{
164 if(this.active){
165 multiply("Active");
166 }else if(this.hovering){
167 multiply("Hover");
168 }
169 }
170
171 if(this._focused){
172 multiply("Focused");
173 }
174
175 // Remove old state classes and add new ones.
176 // For performance concerns we only write into domNode.className once.
177 var tn = this.stateNode || this.domNode,
178 classHash = {}; // set of all classes (state and otherwise) for node
179
180 dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
181
182 if("_stateClasses" in this){
183 dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
184 }
185
186 dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
187
188 var newClasses = [];
189 for(var c in classHash){
190 newClasses.push(c);
191 }
192 tn.className = newClasses.join(" ");
193
194 this._stateClasses = newStateClasses;
195 },
196
197 _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
198 // summary:
199 // Track mouse/focus events on specified node and set CSS class on that node to indicate
200 // current state. Usually not called directly, but via cssStateNodes attribute.
201 // description:
202 // Given class=foo, will set the following CSS class on the node
203 // - fooActive: if the user is currently pressing down the mouse button while over the node
204 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
205 // - fooFocus: if the node is focused
206 //
207 // Note that it won't set any classes if the widget is disabled.
208 // node: DomNode
209 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
210 // is handled specially and automatically just by mixing in this class.
211 // clazz: String
212 // CSS class name (ex: dijitSliderUpArrow).
213
214 // Current state of node (initially false)
215 // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
216 var hovering=false, active=false, focused=false;
217
218 var self = this,
219 cn = dojo.hitch(this, "connect", node);
220
221 function setClass(){
222 var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
223 dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
224 dojo.toggleClass(node, clazz+"Active", active && !disabled);
225 dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
226 }
227
228 // Mouse
229 cn("onmouseenter", function(){
230 hovering = true;
231 setClass();
232 });
233 cn("onmouseleave", function(){
234 hovering = false;
235 active = false;
236 setClass();
237 });
238 cn("onmousedown", function(){
239 active = true;
240 setClass();
241 });
242 cn("onmouseup", function(){
243 active = false;
244 setClass();
245 });
246
247 // Focus
248 cn("onfocus", function(){
249 focused = true;
250 setClass();
251 });
252 cn("onblur", function(){
253 focused = false;
254 setClass();
255 });
256
257 // Just in case widget is enabled/disabled while it has focus/hover/active state.
258 // Maybe this is overkill.
259 this.watch("disabled", setClass);
260 this.watch("readOnly", setClass);
261 }
262 });
263
264 }