]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/layout/StackController.js.uncompressed.js
1 define("dijit/layout/StackController", [
2 "dojo/_base/array", // array.forEach array.indexOf array.map
3 "dojo/_base/declare", // declare
4 "dojo/_base/event", // event.stop
6 "dojo/_base/lang", // lang.getObject
7 "dojo/_base/sniff", // has("ie")
8 "../focus", // focus.focus()
9 "../registry", // registry.byId
13 "../form/ToggleButton",
14 "dojo/i18n!../nls/common"
15 ], function(array
, declare
, event
, keys
, lang
, has
,
16 focus
, registry
, _Widget
, _TemplatedMixin
, _Container
, ToggleButton
){
19 var _Widget = dijit._Widget;
20 var _TemplatedMixin = dijit._TemplatedMixin;
21 var _Container = dijit._Container;
22 var ToggleButton = dijit.form.ToggleButton;
26 // dijit/layout/StackController
28 // Set of buttons to select a page in a `dijit.layout.StackContainer`
30 var StackButton
= declare("dijit.layout._StackButton", ToggleButton
, {
32 // Internal widget used by StackContainer.
34 // The button-like or tab-like object you click to select or delete a page
38 // Override _FormWidget.tabIndex.
39 // StackContainer buttons are not in the tab order by default.
40 // Probably we should be calling this.startupKeyNavChildren() instead.
43 // closeButton: Boolean
44 // When true, display close button for this tab
47 _setCheckedAttr: function(/*Boolean*/ value
, /*Boolean?*/ priorityChange
){
48 this.inherited(arguments
);
49 this.focusNode
.removeAttribute("aria-pressed");
52 buildRendering: function(/*Event*/ evt
){
53 this.inherited(arguments
);
54 (this.focusNode
|| this.domNode
).setAttribute("role", "tab");
57 onClick: function(/*Event*/ /*===== evt =====*/){
59 // This is for TabContainer where the tabs are <span> rather than button,
60 // so need to set focus explicitly (on some browsers)
61 // Note that you shouldn't override this method, but you can connect to it.
62 focus
.focus(this.focusNode
);
64 // ... now let StackController catch the event and tell me what to do
67 onClickCloseButton: function(/*Event*/ evt
){
69 // StackContainer connects to this function; if your widget contains a close button
70 // then clicking it should call this function.
71 // Note that you shouldn't override this method, but you can connect to it.
72 evt
.stopPropagation();
77 var StackController
= declare("dijit.layout.StackController", [_Widget
, _TemplatedMixin
, _Container
], {
79 // Set of buttons to select a page in a `dijit.layout.StackContainer`
81 // Monitors the specified StackContainer, and whenever a page is
82 // added, deleted, or selected, updates itself accordingly.
84 baseClass
: "dijitStackController",
86 templateString
: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
88 // containerId: [const] String
89 // The id of the page container that I point to
92 // buttonWidget: [const] Constructor
93 // The button widget to create to correspond to each page
94 buttonWidget
: StackButton
,
96 constructor: function(){
97 this.pane2button
= {}; // mapping from pane id to buttons
98 this.pane2connects
= {}; // mapping from pane id to this.connect() handles
99 this.pane2watches
= {}; // mapping from pane id to watch() handles
102 postCreate: function(){
103 this.inherited(arguments
);
105 // Listen to notifications from StackContainer
106 this.subscribe(this.containerId
+"-startup", "onStartup");
107 this.subscribe(this.containerId
+"-addChild", "onAddChild");
108 this.subscribe(this.containerId
+"-removeChild", "onRemoveChild");
109 this.subscribe(this.containerId
+"-selectChild", "onSelectChild");
110 this.subscribe(this.containerId
+"-containerKeyPress", "onContainerKeyPress");
113 onStartup: function(/*Object*/ info
){
115 // Called after StackContainer has finished initializing
118 array
.forEach(info
.children
, this.onAddChild
, this);
120 // Show button corresponding to selected pane (unless selected
121 // is null because there are no panes)
122 this.onSelectChild(info
.selected
);
127 for(var pane
in this.pane2button
){
128 this.onRemoveChild(registry
.byId(pane
));
130 this.inherited(arguments
);
133 onAddChild: function(/*dijit._Widget*/ page
, /*Integer?*/ insertIndex
){
135 // Called whenever a page is added to the container.
136 // Create button corresponding to the page.
140 // create an instance of the button widget
141 // (remove typeof buttonWidget == string support in 2.0)
142 var cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
143 var button
= new cls({
144 id
: this.id
+ "_" + page
.id
,
148 textDir
: page
.textDir
,
149 showLabel
: page
.showTitle
,
150 iconClass
: page
.iconClass
,
151 closeButton
: page
.closable
,
154 button
.focusNode
.setAttribute("aria-selected", "false");
157 // map from page attribute to corresponding tab button attribute
158 var pageAttrList
= ["title", "showTitle", "iconClass", "closable", "tooltip"],
159 buttonAttrList
= ["label", "showLabel", "iconClass", "closeButton", "title"];
161 // watch() so events like page title changes are reflected in tab button
162 this.pane2watches
[page
.id
] = array
.map(pageAttrList
, function(pageAttr
, idx
){
163 return page
.watch(pageAttr
, function(name
, oldVal
, newVal
){
164 button
.set(buttonAttrList
[idx
], newVal
);
168 // connections so that clicking a tab button selects the corresponding page
169 this.pane2connects
[page
.id
] = [
170 this.connect(button
, 'onClick', lang
.hitch(this,"onButtonClick", page
)),
171 this.connect(button
, 'onClickCloseButton', lang
.hitch(this,"onCloseButtonClick", page
))
174 this.addChild(button
, insertIndex
);
175 this.pane2button
[page
.id
] = button
;
176 page
.controlButton
= button
; // this value might be overwritten if two tabs point to same container
177 if(!this._currentChild
){ // put the first child into the tab order
178 button
.focusNode
.setAttribute("tabIndex", "0");
179 button
.focusNode
.setAttribute("aria-selected", "true");
180 this._currentChild
= page
;
182 // make sure all tabs have the same length
183 if(!this.isLeftToRight() && has("ie") && this._rectifyRtlTabList
){
184 this._rectifyRtlTabList();
188 onRemoveChild: function(/*dijit._Widget*/ page
){
190 // Called whenever a page is removed from the container.
191 // Remove the button corresponding to the page.
195 if(this._currentChild
=== page
){ this._currentChild
= null; }
197 // disconnect/unwatch connections/watches related to page being removed
198 array
.forEach(this.pane2connects
[page
.id
], lang
.hitch(this, "disconnect"));
199 delete this.pane2connects
[page
.id
];
200 array
.forEach(this.pane2watches
[page
.id
], function(w
){ w
.unwatch(); });
201 delete this.pane2watches
[page
.id
];
203 var button
= this.pane2button
[page
.id
];
205 this.removeChild(button
);
206 delete this.pane2button
[page
.id
];
209 delete page
.controlButton
;
212 onSelectChild: function(/*dijit._Widget*/ page
){
214 // Called when a page has been selected in the StackContainer, either by me or by another StackController
220 if(this._currentChild
){
221 var oldButton
=this.pane2button
[this._currentChild
.id
];
222 oldButton
.set('checked', false);
223 oldButton
.focusNode
.setAttribute("aria-selected", "false");
224 oldButton
.focusNode
.setAttribute("tabIndex", "-1");
227 var newButton
=this.pane2button
[page
.id
];
228 newButton
.set('checked', true);
229 newButton
.focusNode
.setAttribute("aria-selected", "true");
230 this._currentChild
= page
;
231 newButton
.focusNode
.setAttribute("tabIndex", "0");
232 var container
= registry
.byId(this.containerId
);
233 container
.containerNode
.setAttribute("aria-labelledby", newButton
.id
);
236 onButtonClick: function(/*dijit._Widget*/ page
){
238 // Called whenever one of my child buttons is pressed in an attempt to select a page
242 if(this._currentChild
.id
=== page
.id
) {
243 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
244 var button
=this.pane2button
[page
.id
];
245 button
.set('checked', true);
247 var container
= registry
.byId(this.containerId
);
248 container
.selectChild(page
);
251 onCloseButtonClick: function(/*dijit._Widget*/ page
){
253 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
257 var container
= registry
.byId(this.containerId
);
258 container
.closeChild(page
);
259 if(this._currentChild
){
260 var b
= this.pane2button
[this._currentChild
.id
];
262 focus
.focus(b
.focusNode
|| b
.domNode
);
267 // TODO: this is a bit redundant with forward, back api in StackContainer
268 adjacent: function(/*Boolean*/ forward
){
270 // Helper for onkeypress to find next/previous button
274 if(!this.isLeftToRight() && (!this.tabPosition
|| /top|bottom/.test(this.tabPosition
))){ forward
= !forward
; }
275 // find currently focused button in children array
276 var children
= this.getChildren();
277 var current
= array
.indexOf(children
, this.pane2button
[this._currentChild
.id
]);
278 // pick next button to focus on
279 var offset
= forward
? 1 : children
.length
- 1;
280 return children
[ (current
+ offset
) % children
.length
]; // dijit._Widget
283 onkeypress: function(/*Event*/ e
){
285 // Handle keystrokes on the page list, for advancing to next/previous button
286 // and closing the current page if the page is closable.
290 if(this.disabled
|| e
.altKey
){ return; }
292 if(e
.ctrlKey
|| !e
._djpage
){
293 switch(e
.charOrCode
){
294 case keys
.LEFT_ARROW
:
296 if(!e
._djpage
){ forward
= false; }
299 if(e
.ctrlKey
){ forward
= false; }
301 case keys
.RIGHT_ARROW
:
302 case keys
.DOWN_ARROW
:
303 if(!e
._djpage
){ forward
= true; }
306 if(e
.ctrlKey
){ forward
= true; }
310 var children
= this.getChildren();
311 if(children
&& children
.length
){
312 children
[e
.charOrCode
== keys
.HOME
? 0 : children
.length
-1].onClick();
317 if(this._currentChild
.closable
){
318 this.onCloseButtonClick(this._currentChild
);
324 if(e
.charOrCode
=== keys
.TAB
){
325 this.adjacent(!e
.shiftKey
).onClick();
327 }else if(e
.charOrCode
== "w"){
328 if(this._currentChild
.closable
){
329 this.onCloseButtonClick(this._currentChild
);
331 event
.stop(e
); // avoid browser tab closing.
335 // handle next/previous page navigation (left/right arrow, etc.)
336 if(forward
!== null){
337 this.adjacent(forward
).onClick();
343 onContainerKeyPress: function(/*Object*/ info
){
345 // Called when there was a keypress on the container
348 info
.e
._djpage
= info
.page
;
349 this.onkeypress(info
.e
);
353 StackController
.StackButton
= StackButton
; // for monkey patching
355 return StackController
;