source: Dev/branches/rest-dojo-ui/client/dojox/layout/ToggleSplitter.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 9.4 KB
Line 
1define("dojox/layout/ToggleSplitter", ["dojo", "dijit", "dijit/layout/BorderContainer"], function(dojo, dijit) {
2
3dojo.experimental("dojox.layout.ToggleSplitter");
4
5dojo.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
29        // state: String
30        //              the initial and current state of the splitter (and its attached pane)
31        //              It has three values: full, collapsed (optional), closed
32        state: "full",
33
34        // _closedSize: String
35        //      the css height/width value to apply by default when the attached pane is closed
36        _closedSize: "0",
37
38        baseClass: "dojoxToggleSplitter",
39
40        templateString: '<div class="dijitSplitter dojoxToggleSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse">' +
41                                                '<div dojoAttachPoint="toggleNode" class="dijitSplitterThumb dojoxToggleSplitterIcon" tabIndex="0" role="separator" ' +
42                                                        'dojoAttachEvent="onmousedown:_onToggleNodeMouseDown,onclick:_toggle,onmouseenter:_onToggleNodeMouseMove,onmouseleave:_onToggleNodeMouseMove,onfocus:_onToggleNodeMouseMove,onblur:_onToggleNodeMouseMove">' +
43                                                        '<span class="dojoxToggleSplitterA11y" dojoAttachPoint="a11yText"></span></div>' +
44                                        '</div>',
45
46        postCreate: function(){
47                this.inherited(arguments);
48
49                // add a region css hook so that it can figure out the region correctly
50                var region = this.region;
51                dojo.addClass(this.domNode, this.baseClass + region.charAt(0).toUpperCase() + region.substring(1));
52        },
53
54        startup: function(){
55                this.inherited(arguments);
56
57                // we have to wait until startup to be sure the child exists in the dom
58                // and has non-zero size (if its supposed to be showing)
59                var parentPane = this.child,
60                        paneNode = this.child.domNode,
61                        intPaneSize = dojo.style(paneNode, (this.horizontal ? "height" : "width"));
62
63                this.domNode.setAttribute("aria-controls", paneNode.id);
64
65                // creation of splitters is an opaque process in BorderContainer,
66                // so if we want to get init params, we have to retrieve them from the attached BC child
67                // NOTE: for this to work we have to extend the prototype of dijit._Widget (some more)
68                dojo.forEach(["toggleSplitterState", "toggleSplitterFullSize", "toggleSplitterCollapsedSize"], function(name){
69                        var pname = name.substring("toggleSplitter".length);
70                        pname = pname.charAt(0).toLowerCase() + pname.substring(1);
71                        if(name in this.child){
72                                this[pname] = this.child[name];
73                        }
74                }, this);
75
76                if(!this.fullSize){
77                        // Store the current size as the fullSize if none was provided
78                        // dojo.style always returns a integer (pixel) value for height/width
79                        // use an arbitrary default if a pane was initialized closed and no fullSize provided
80                        // If collapsedSize is not specified, collapsed state does not exist.
81                        this.fullSize = this.state == "full" ? intPaneSize + "px" : "75px";
82                }
83
84                this._openStyleProps = this._getStyleProps(paneNode, "full");
85
86                // update state
87                this._started = true;
88                this.set("state", this.state);
89
90                return this;
91        },
92
93        _onKeyPress: function(evt){
94                if(this.state == "full"){
95                        this.inherited(arguments);
96                }
97                if(evt.charCode == dojo.keys.SPACE || evt.keyCode == dojo.keys.ENTER){
98                        this._toggle(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(/*Strring*/ 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
295});
Note: See TracBrowser for help on using the repository browser.