/* 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"]){ //_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", [], { // 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); } }); }