]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/layout/StackController.js
remove call-by-reference to comply with php 5.4
[tt-rss.git] / lib / dijit / layout / StackController.js
CommitLineData
2f01fe57 1/*
81bea17a 2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
2f01fe57
AD
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5*/
6
7
81bea17a
AD
8if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9dojo._hasResource["dijit.layout.StackController"] = true;
2f01fe57
AD
10dojo.provide("dijit.layout.StackController");
11dojo.require("dijit._Widget");
12dojo.require("dijit._Templated");
13dojo.require("dijit._Container");
14dojo.require("dijit.form.ToggleButton");
81bea17a
AD
15dojo.requireLocalization("dijit", "common", null, "ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
16
17
18dojo.declare(
19 "dijit.layout.StackController",
20 [dijit._Widget, dijit._Templated, dijit._Container],
21 {
22 // summary:
23 // Set of buttons to select a page in a page list.
24 // description:
25 // Monitors the specified StackContainer, and whenever a page is
26 // added, deleted, or selected, updates itself accordingly.
27
28 templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
29
30 // containerId: [const] String
31 // The id of the page container that I point to
32 containerId: "",
33
34 // buttonWidget: [const] String
35 // The name of the button widget to create to correspond to each page
36 buttonWidget: "dijit.layout._StackButton",
37
38 constructor: function(){
39 this.pane2button = {}; // mapping from pane id to buttons
40 this.pane2connects = {}; // mapping from pane id to this.connect() handles
41 this.pane2watches = {}; // mapping from pane id to watch() handles
42 },
43
44 buildRendering: function(){
45 this.inherited(arguments);
46 dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above.
47 },
48
49 postCreate: function(){
50 this.inherited(arguments);
51
52 // Listen to notifications from StackContainer
53 this.subscribe(this.containerId+"-startup", "onStartup");
54 this.subscribe(this.containerId+"-addChild", "onAddChild");
55 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
56 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
57 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
58 },
59
60 onStartup: function(/*Object*/ info){
61 // summary:
62 // Called after StackContainer has finished initializing
63 // tags:
64 // private
65 dojo.forEach(info.children, this.onAddChild, this);
66 if(info.selected){
67 // Show button corresponding to selected pane (unless selected
68 // is null because there are no panes)
69 this.onSelectChild(info.selected);
70 }
71 },
72
73 destroy: function(){
74 for(var pane in this.pane2button){
75 this.onRemoveChild(dijit.byId(pane));
76 }
77 this.inherited(arguments);
78 },
79
80 onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
81 // summary:
82 // Called whenever a page is added to the container.
83 // Create button corresponding to the page.
84 // tags:
85 // private
86
87 // create an instance of the button widget
88 var cls = dojo.getObject(this.buttonWidget);
89 var button = new cls({
90 id: this.id + "_" + page.id,
91 label: page.title,
92 dir: page.dir,
93 lang: page.lang,
94 showLabel: page.showTitle,
95 iconClass: page.iconClass,
96 closeButton: page.closable,
97 title: page.tooltip
98 });
99 dijit.setWaiState(button.focusNode,"selected", "false");
100
101
102 // map from page attribute to corresponding tab button attribute
103 var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
104 buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
105
106 // watch() so events like page title changes are reflected in tab button
107 this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
108 return page.watch(pageAttr, function(name, oldVal, newVal){
109 button.set(buttonAttrList[idx], newVal);
110 });
111 });
112
113 // connections so that clicking a tab button selects the corresponding page
114 this.pane2connects[page.id] = [
115 this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
116 this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
117 ];
118
119 this.addChild(button, insertIndex);
120 this.pane2button[page.id] = button;
121 page.controlButton = button; // this value might be overwritten if two tabs point to same container
122 if(!this._currentChild){ // put the first child into the tab order
123 button.focusNode.setAttribute("tabIndex", "0");
124 dijit.setWaiState(button.focusNode, "selected", "true");
125 this._currentChild = page;
126 }
127 // make sure all tabs have the same length
128 if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
129 this._rectifyRtlTabList();
130 }
131 },
132
133 onRemoveChild: function(/*dijit._Widget*/ page){
134 // summary:
135 // Called whenever a page is removed from the container.
136 // Remove the button corresponding to the page.
137 // tags:
138 // private
139
140 if(this._currentChild === page){ this._currentChild = null; }
141
142 // disconnect/unwatch connections/watches related to page being removed
143 dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
144 delete this.pane2connects[page.id];
145 dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
146 delete this.pane2watches[page.id];
147
148 var button = this.pane2button[page.id];
149 if(button){
150 this.removeChild(button);
151 delete this.pane2button[page.id];
152 button.destroy();
153 }
154 delete page.controlButton;
155 },
156
157 onSelectChild: function(/*dijit._Widget*/ page){
158 // summary:
159 // Called when a page has been selected in the StackContainer, either by me or by another StackController
160 // tags:
161 // private
162
163 if(!page){ return; }
164
165 if(this._currentChild){
166 var oldButton=this.pane2button[this._currentChild.id];
167 oldButton.set('checked', false);
168 dijit.setWaiState(oldButton.focusNode, "selected", "false");
169 oldButton.focusNode.setAttribute("tabIndex", "-1");
170 }
171
172 var newButton=this.pane2button[page.id];
173 newButton.set('checked', true);
174 dijit.setWaiState(newButton.focusNode, "selected", "true");
175 this._currentChild = page;
176 newButton.focusNode.setAttribute("tabIndex", "0");
177 var container = dijit.byId(this.containerId);
178 dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
179 },
180
181 onButtonClick: function(/*dijit._Widget*/ page){
182 // summary:
183 // Called whenever one of my child buttons is pressed in an attempt to select a page
184 // tags:
185 // private
186
187 var container = dijit.byId(this.containerId);
188 container.selectChild(page);
189 },
190
191 onCloseButtonClick: function(/*dijit._Widget*/ page){
192 // summary:
193 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
194 // tags:
195 // private
196
197 var container = dijit.byId(this.containerId);
198 container.closeChild(page);
199 if(this._currentChild){
200 var b = this.pane2button[this._currentChild.id];
201 if(b){
202 dijit.focus(b.focusNode || b.domNode);
203 }
204 }
205 },
206
207 // TODO: this is a bit redundant with forward, back api in StackContainer
208 adjacent: function(/*Boolean*/ forward){
209 // summary:
210 // Helper for onkeypress to find next/previous button
211 // tags:
212 // private
213
214 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
215 // find currently focused button in children array
216 var children = this.getChildren();
217 var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
218 // pick next button to focus on
219 var offset = forward ? 1 : children.length - 1;
220 return children[ (current + offset) % children.length ]; // dijit._Widget
221 },
222
223 onkeypress: function(/*Event*/ e){
224 // summary:
225 // Handle keystrokes on the page list, for advancing to next/previous button
226 // and closing the current page if the page is closable.
227 // tags:
228 // private
229
230 if(this.disabled || e.altKey ){ return; }
231 var forward = null;
232 if(e.ctrlKey || !e._djpage){
233 var k = dojo.keys;
234 switch(e.charOrCode){
235 case k.LEFT_ARROW:
236 case k.UP_ARROW:
237 if(!e._djpage){ forward = false; }
238 break;
239 case k.PAGE_UP:
240 if(e.ctrlKey){ forward = false; }
241 break;
242 case k.RIGHT_ARROW:
243 case k.DOWN_ARROW:
244 if(!e._djpage){ forward = true; }
245 break;
246 case k.PAGE_DOWN:
247 if(e.ctrlKey){ forward = true; }
248 break;
249 case k.HOME:
250 case k.END:
251 var children = this.getChildren();
252 if(children && children.length){
253 children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
254 }
255 dojo.stopEvent(e);
256 break;
257 case k.DELETE:
258 if(this._currentChild.closable){
259 this.onCloseButtonClick(this._currentChild);
260 }
261 dojo.stopEvent(e);
262 break;
263 default:
264 if(e.ctrlKey){
265 if(e.charOrCode === k.TAB){
266 this.adjacent(!e.shiftKey).onClick();
267 dojo.stopEvent(e);
268 }else if(e.charOrCode == "w"){
269 if(this._currentChild.closable){
270 this.onCloseButtonClick(this._currentChild);
271 }
272 dojo.stopEvent(e); // avoid browser tab closing.
273 }
274 }
275 }
276 // handle next/previous page navigation (left/right arrow, etc.)
277 if(forward !== null){
278 this.adjacent(forward).onClick();
279 dojo.stopEvent(e);
280 }
281 }
282 },
283
284 onContainerKeyPress: function(/*Object*/ info){
285 // summary:
286 // Called when there was a keypress on the container
287 // tags:
288 // private
289 info.e._djpage = info.page;
290 this.onkeypress(info.e);
291 }
292 });
293
294
295dojo.declare("dijit.layout._StackButton",
296 dijit.form.ToggleButton,
297 {
298 // summary:
299 // Internal widget used by StackContainer.
300 // description:
301 // The button-like or tab-like object you click to select or delete a page
302 // tags:
303 // private
304
305 // Override _FormWidget.tabIndex.
306 // StackContainer buttons are not in the tab order by default.
307 // Probably we should be calling this.startupKeyNavChildren() instead.
308 tabIndex: "-1",
309
310 buildRendering: function(/*Event*/ evt){
311 this.inherited(arguments);
312 dijit.setWaiRole((this.focusNode || this.domNode), "tab");
313 },
314
315 onClick: function(/*Event*/ evt){
316 // summary:
317 // This is for TabContainer where the tabs are <span> rather than button,
318 // so need to set focus explicitly (on some browsers)
319 // Note that you shouldn't override this method, but you can connect to it.
320 dijit.focus(this.focusNode);
321
322 // ... now let StackController catch the event and tell me what to do
323 },
324
325 onClickCloseButton: function(/*Event*/ evt){
326 // summary:
327 // StackContainer connects to this function; if your widget contains a close button
328 // then clicking it should call this function.
329 // Note that you shouldn't override this method, but you can connect to it.
330 evt.stopPropagation();
331 }
332 });
333
2f01fe57 334}