source: Dev/trunk/src/client/dojox/layout/ToggleSplitter.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 9.5 KB
Line 
1define("dojox/layout/ToggleSplitter", ["dojo", "dijit", "dijit/layout/BorderContainer"], function(dojo, dijit) {
2
3dojo.experimental("dojox.layout.ToggleSplitter");
4
5var ToggleSplitter = dojo.declare("dojox.layout.ToggleSplitter", dijit.layout._Splitter, {
6        // summary:
7        //              A draggable and clickable spacer between two items in a `dijit.layout.BorderContainer`.
8        // description:
9        //              This is instantiated by `dijit.layout.BorderContainer`. Users should not
10        //              create it directly.
11        // tags:
12        //              private
13
14
15        // container: [const] dijit/layout/BorderContainer
16        //              Pointer to the parent BorderContainer
17        container: null,
18
19        // child: [const] dijit/layout/_LayoutWidget
20        //              Pointer to the pane associated with this splitter
21        child: null,
22
23        // region: [const] String
24        //              Region of pane associated with this splitter.
25        //              "top", "bottom", "left", "right".
26        region: null,
27
28        // state: String
29        //              the initial and current state of the splitter (and its attached pane)
30        //              It has three values: full, collapsed (optional), closed
31        state: "full",
32
33        // _closedSize: String
34        //      the css height/width value to apply by default when the attached pane is closed
35        _closedSize: "0",
36
37        baseClass: "dojoxToggleSplitter",
38
39        templateString: '<div class="dijitSplitter dojoxToggleSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse">' +
40                                                '<div dojoAttachPoint="toggleNode" class="dijitSplitterThumb dojoxToggleSplitterIcon" tabIndex="0" role="separator" ' +
41                                                        'dojoAttachEvent="onmousedown:_onToggleNodeMouseDown,onclick:_toggle,onmouseenter:_onToggleNodeMouseMove,onmouseleave:_onToggleNodeMouseMove,onfocus:_onToggleNodeMouseMove,onblur:_onToggleNodeMouseMove">' +
42                                                        '<span class="dojoxToggleSplitterA11y" dojoAttachPoint="a11yText"></span></div>' +
43                                        '</div>',
44
45        postCreate: function(){
46                this.inherited(arguments);
47
48                // add a region css hook so that it can figure out the region correctly
49                var region = this.region;
50                dojo.addClass(this.domNode, this.baseClass + region.charAt(0).toUpperCase() + region.substring(1));
51        },
52
53        startup: function(){
54                this.inherited(arguments);
55
56                // we have to wait until startup to be sure the child exists in the dom
57                // and has non-zero size (if its supposed to be showing)
58                var parentPane = this.child,
59                        paneNode = this.child.domNode,
60                        intPaneSize = dojo.style(paneNode, (this.horizontal ? "height" : "width"));
61
62                this.domNode.setAttribute("aria-controls", paneNode.id);
63
64                // creation of splitters is an opaque process in BorderContainer,
65                // so if we want to get init params, we have to retrieve them from the attached BC child
66                // NOTE: for this to work we have to extend the prototype of dijit._Widget (some more)
67                dojo.forEach(["toggleSplitterState", "toggleSplitterFullSize", "toggleSplitterCollapsedSize"], function(name){
68                        var pname = name.substring("toggleSplitter".length);
69                        pname = pname.charAt(0).toLowerCase() + pname.substring(1);
70                        if(name in this.child){
71                                this[pname] = this.child[name];
72                        }
73                }, this);
74
75                if(!this.fullSize){
76                        // Store the current size as the fullSize if none was provided
77                        // dojo.style always returns a integer (pixel) value for height/width
78                        // use an arbitrary default if a pane was initialized closed and no fullSize provided
79                        // If collapsedSize is not specified, collapsed state does not exist.
80                        this.fullSize = this.state == "full" ? intPaneSize + "px" : "75px";
81                }
82
83                this._openStyleProps = this._getStyleProps(paneNode, "full");
84
85                // update state
86                this._started = true;
87                this.set("state", this.state);
88
89                return this;
90        },
91
92        _onKeyPress: function(evt){
93                if(this.state == "full"){
94                        this.inherited(arguments);
95                }
96                if(evt.charCode == dojo.keys.SPACE || evt.keyCode == dojo.keys.ENTER){
97                        this._toggle(evt);
98                        dojo.stopEvent(evt);
99                }
100        },
101
102        _onToggleNodeMouseDown: function(evt){
103                dojo.stopEvent(evt);
104                this.toggleNode.focus();
105        },
106
107        _startDrag: function(e){
108                if(this.state == "full"){
109                        this.inherited(arguments);
110                }
111        },
112
113        _stopDrag: function(e){
114                this.inherited(arguments);
115                this.toggleNode.blur();
116        },
117
118        _toggle: function(evt){
119                var state;
120                switch(this.state){
121                        case "full":
122                                state = this.collapsedSize ? "collapsed" : "closed";
123                                break;
124                        case "collapsed":
125                                state = "closed";
126                                break;
127                        default:
128                                state = "full";
129                }
130                this.set("state", state);
131        },
132
133        _onToggleNodeMouseMove: function(evt){
134                var baseClass = this.baseClass,
135                        toggleNode = this.toggleNode,
136                        on = this.state == "full" || this.state == "collapsed",
137                        leave = evt.type == "mouseout" || evt.type == "blur";
138
139                dojo.toggleClass(toggleNode, baseClass + "IconOpen", leave && on);
140                dojo.toggleClass(toggleNode, baseClass + "IconOpenHover", !leave && on);
141                dojo.toggleClass(toggleNode, baseClass + "IconClosed", leave && !on);
142                dojo.toggleClass(toggleNode, baseClass + "IconClosedHover", !leave && !on);
143        },
144
145        _handleOnChange: function(preState){
146                // summary:
147                //              Effect the state change with the new value of this.state
148                var paneNode = this.child.domNode,
149                        openProps, paneStyle,
150                        dim = this.horizontal ? "height" : "width";
151
152                if(this.state == "full"){
153                        // change to full open state
154                        var styleProps = dojo.mixin({
155                                display: "block",
156                                overflow: "auto",
157                                visibility: "visible"
158                        }, this._openStyleProps);
159                        styleProps[dim] = (this._openStyleProps && this._openStyleProps[dim]) ? this._openStyleProps[dim] : this.fullSize;
160
161                        dojo.style(this.domNode, "cursor", "");
162                        dojo.style(paneNode, styleProps);
163                }else if(this.state == "collapsed"){
164                        paneStyle  = dojo.getComputedStyle(paneNode);
165                        openProps = this._getStyleProps(paneNode, "full", paneStyle);
166                        this._openStyleProps = openProps;
167
168                        dojo.style(this.domNode, "cursor", "auto");
169                        dojo.style(paneNode, dim, this.collapsedSize);
170                }else{
171                        // change to closed state
172                        if(!this.collapsedSize){
173                                paneStyle  = dojo.getComputedStyle(paneNode);
174                                openProps = this._getStyleProps(paneNode, "full", paneStyle);
175                                this._openStyleProps = openProps;
176                        }
177                        var closedProps = this._getStyleProps(paneNode, "closed", paneStyle);
178
179                        dojo.style(this.domNode, "cursor", "auto");
180                        dojo.style(paneNode, closedProps);
181                }
182                this._setStateClass();
183                if(this.container._started){
184                        this.container._layoutChildren(this.region);
185                }
186        },
187
188        _getStyleProps: function(paneNode, state, paneStyle){
189                // summary:
190                //              Create an object with the style property name: values
191                //              that will need to be applied to the child pane render the given state
192                if(!paneStyle){
193                        paneStyle  = dojo.getComputedStyle(paneNode);
194                }
195                var styleProps = {},
196                        dim = this.horizontal ? "height" : "width";
197
198                styleProps["overflow"] = (state != "closed") ? paneStyle["overflow"] : "hidden";
199                styleProps["visibility"] = (state != "closed") ? paneStyle["visibility"] : "hidden";
200
201                // Use the inline width/height style value, in preference to the computedStyle
202                // for the open width/height
203                styleProps[dim] = (state != "closed") ? paneNode.style[dim] || paneStyle[dim] : this._closedSize;
204
205                // We include the padding, border, margin width values for restoring on state full open
206                var edgeNames = ["Top", "Right", "Bottom", "Left"];
207                dojo.forEach(["padding", "margin", "border"], function(pname){
208                        for(var i = 0; i < edgeNames.length; i++){
209                                var fullName = pname + edgeNames[i];
210                                if(pname == "border"){
211                                        fullName += "Width";
212                                }
213                                if(undefined !== paneStyle[fullName]){
214                                        styleProps[fullName] = (state != "closed") ? paneStyle[fullName] : 0;
215                                }
216                        }
217                });
218
219                return styleProps;
220        },
221
222        _setStateClass: function(){
223                // summary:
224                //              Apply the appropriate classes for the current open state
225                var arrow = "&#9652", region = this.region.toLowerCase(),
226                        baseClass = this.baseClass,
227                        toggleNode = this.toggleNode,
228                        on = this.state == "full" || this.state == "collapsed",
229                        focused = this.focused;
230
231                dojo.toggleClass(toggleNode, baseClass + "IconOpen", on && !focused);
232                dojo.toggleClass(toggleNode, baseClass + "IconClosed", !on && !focused);
233                dojo.toggleClass(toggleNode, baseClass + "IconOpenHover", on && focused);
234                dojo.toggleClass(toggleNode, baseClass + "IconClosedHover", !on && focused);
235
236                // For a11y
237                if(region == "top" && on || region == "bottom" && !on){
238                        arrow = "&#9650";
239                }else if(region == "top" && !on || region == "bottom" && on){
240                        arrow = "&#9660";
241                }else if(region == "right" && on || region == "left" && !on){
242                        arrow = "&#9654";
243                }else if(region == "right" && !on || region == "left" && on){
244                        arrow = "&#9664";
245                }
246
247                this.a11yText.innerHTML = arrow;
248        },
249
250        _setStateAttr: function(/*String*/ state){
251                // summary:
252                //              setter for the state property
253                if(!this._started) {
254                        return;
255                }
256                var preState = this.state;
257                this.state = state;
258
259                this._handleOnChange(preState);
260                var evtName;
261                switch(state){
262                        case "full":
263                                this.domNode.setAttribute("aria-expanded", true);
264                                evtName = "onOpen";
265                                break;
266                        case "collapsed":
267                                this.domNode.setAttribute("aria-expanded", true);
268                                evtName = "onCollapsed";
269                                break;
270                        default:
271                                this.domNode.setAttribute("aria-expanded", false);
272                                evtName = "onClosed";
273                }
274                this[evtName](this.child);
275        },
276
277        onOpen: function(pane){ /*Stub*/ },
278        onCollapsed: function(pane){ /*Stub*/ },
279        onClosed: function(pane){ /*Stub*/ }
280});
281
282// As BC places no constraints on what kind of widgets can be children
283// we have to extend the base class to ensure the properties we need can be set (both in markup and programatically)
284dojo.extend(dijit._Widget, {
285        // toggleSplitterOpen: Boolean
286        toggleSplitterState: "full",
287
288        // toggleSplitterClosedThreshold: String
289        //              A css size value (e.g. "100px")
290        toggleSplitterFullSize: "",
291
292        toggleSplitterCollapsedSize: ""
293});
294
295return ToggleSplitter;
296
297});
Note: See TracBrowser for help on using the repository browser.