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