]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/layout/StackContainer.js.uncompressed.js
make precache_headlines_idle() start slower
[tt-rss.git] / lib / dijit / layout / StackContainer.js.uncompressed.js
CommitLineData
1354d172
AD
1define("dijit/layout/StackContainer", [
2 "dojo/_base/array", // array.forEach array.indexOf array.some
3 "dojo/cookie", // cookie
4 "dojo/_base/declare", // declare
5 "dojo/dom-class", // domClass.add domClass.replace
6 "dojo/_base/kernel", // kernel.isAsync
7 "dojo/_base/lang", // lang.extend
8 "dojo/ready",
9 "dojo/topic", // publish
10 "../registry", // registry.byId
11 "../_WidgetBase",
12 "./_LayoutWidget",
13 "dojo/i18n!../nls/common"
14], function(array, cookie, declare, domClass, kernel, lang, ready, topic,
15 registry, _WidgetBase, _LayoutWidget){
16
17/*=====
18var _WidgetBase = dijit._WidgetBase;
19var _LayoutWidget = dijit.layout._LayoutWidget;
20var StackController = dijit.layout.StackController;
21=====*/
22
23// module:
24// dijit/layout/StackContainer
25// summary:
26// A container that has multiple children, but shows only one child at a time.
27
28// Back compat w/1.6, remove for 2.0
29if(!kernel.isAsync){
30 ready(0, function(){
31 var requires = ["dijit/layout/StackController"];
32 require(requires); // use indirection so modules not rolled into a build
33 });
34}
35
36// These arguments can be specified for the children of a StackContainer.
37// Since any widget can be specified as a StackContainer child, mix them
38// into the base widget class. (This is a hack, but it's effective.)
39lang.extend(_WidgetBase, {
40 // selected: Boolean
41 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
42 // Specifies that this widget should be the initially displayed pane.
43 // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
44 selected: false,
45
46 // closable: Boolean
47 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
48 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
49 closable: false,
50
51 // iconClass: String
52 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
53 // CSS Class specifying icon to use in label associated with this pane.
54 iconClass: "dijitNoIcon",
55
56 // showTitle: Boolean
57 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
58 // When true, display title of this widget as tab label etc., rather than just using
59 // icon specified in iconClass
60 showTitle: true
61});
62
63return declare("dijit.layout.StackContainer", _LayoutWidget, {
64 // summary:
65 // A container that has multiple children, but shows only
66 // one child at a time
67 //
68 // description:
69 // A container for widgets (ContentPanes, for example) That displays
70 // only one Widget at a time.
71 //
72 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
73 //
74 // Can be base class for container, Wizard, Show, etc.
75
76 // doLayout: Boolean
77 // If true, change the size of my currently displayed child to match my size
78 doLayout: true,
79
80 // persist: Boolean
81 // Remembers the selected child across sessions
82 persist: false,
83
84 baseClass: "dijitStackContainer",
85
86/*=====
87 // selectedChildWidget: [readonly] dijit._Widget
88 // References the currently selected child widget, if any.
89 // Adjust selected child with selectChild() method.
90 selectedChildWidget: null,
91=====*/
92
93 buildRendering: function(){
94 this.inherited(arguments);
95 domClass.add(this.domNode, "dijitLayoutContainer");
96 this.containerNode.setAttribute("role", "tabpanel");
97 },
98
99 postCreate: function(){
100 this.inherited(arguments);
101 this.connect(this.domNode, "onkeypress", this._onKeyPress);
102 },
103
104 startup: function(){
105 if(this._started){ return; }
106
107 var children = this.getChildren();
108
109 // Setup each page panel to be initially hidden
110 array.forEach(children, this._setupChild, this);
111
112 // Figure out which child to initially display, defaulting to first one
113 if(this.persist){
114 this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
115 }else{
116 array.some(children, function(child){
117 if(child.selected){
118 this.selectedChildWidget = child;
119 }
120 return child.selected;
121 }, this);
122 }
123 var selected = this.selectedChildWidget;
124 if(!selected && children[0]){
125 selected = this.selectedChildWidget = children[0];
126 selected.selected = true;
127 }
128
129 // Publish information about myself so any StackControllers can initialize.
130 // This needs to happen before this.inherited(arguments) so that for
131 // TabContainer, this._contentBox doesn't include the space for the tab labels.
132 topic.publish(this.id+"-startup", {children: children, selected: selected});
133
134 // Startup each child widget, and do initial layout like setting this._contentBox,
135 // then calls this.resize() which does the initial sizing on the selected child.
136 this.inherited(arguments);
137 },
138
139 resize: function(){
140 // Resize is called when we are first made visible (it's called from startup()
141 // if we are initially visible). If this is the first time we've been made
142 // visible then show our first child.
143 if(!this._hasBeenShown){
144 this._hasBeenShown = true;
145 var selected = this.selectedChildWidget;
146 if(selected){
147 this._showChild(selected);
148 }
149 }
150 this.inherited(arguments);
151 },
152
153 _setupChild: function(/*dijit._Widget*/ child){
154 // Overrides _LayoutWidget._setupChild()
155
156 this.inherited(arguments);
157
158 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
159
160 // remove the title attribute so it doesn't show up when i hover
161 // over a node
162 child.domNode.title = "";
163 },
164
165 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
166 // Overrides _Container.addChild() to do layout and publish events
167
168 this.inherited(arguments);
169
170 if(this._started){
171 topic.publish(this.id+"-addChild", child, insertIndex); // publish
172
173 // in case the tab titles have overflowed from one line to two lines
174 // (or, if this if first child, from zero lines to one line)
175 // TODO: w/ScrollingTabController this is no longer necessary, although
176 // ScrollTabController.resize() does need to get called to show/hide
177 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
178 // If this is updated to not layout [except for initial child added / last child removed], update
179 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
180 this.layout();
181
182 // if this is the first child, then select it
183 if(!this.selectedChildWidget){
184 this.selectChild(child);
185 }
186 }
187 },
188
189 removeChild: function(/*dijit._Widget*/ page){
190 // Overrides _Container.removeChild() to do layout and publish events
191
192 this.inherited(arguments);
193
194 if(this._started){
195 // this will notify any tablists to remove a button; do this first because it may affect sizing
196 topic.publish(this.id + "-removeChild", page); // publish
197 }
198
199 // If all our children are being destroyed than don't run the code below (to select another page),
200 // because we are deleting every page one by one
201 if(this._descendantsBeingDestroyed){ return; }
202
203 // Select new page to display, also updating TabController to show the respective tab.
204 // Do this before layout call because it can affect the height of the TabController.
205 if(this.selectedChildWidget === page){
206 this.selectedChildWidget = undefined;
207 if(this._started){
208 var children = this.getChildren();
209 if(children.length){
210 this.selectChild(children[0]);
211 }
212 }
213 }
214
215 if(this._started){
216 // In case the tab titles now take up one line instead of two lines
217 // (note though that ScrollingTabController never overflows to multiple lines),
218 // or the height has changed slightly because of addition/removal of tab which close icon
219 this.layout();
220 }
221 },
222
223 selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
224 // summary:
225 // Show the given widget (which must be one of my children)
226 // page:
227 // Reference to child widget or id of child widget
228
229 page = registry.byId(page);
230
231 if(this.selectedChildWidget != page){
232 // Deselect old page and select new one
233 var d = this._transition(page, this.selectedChildWidget, animate);
234 this._set("selectedChildWidget", page);
235 topic.publish(this.id+"-selectChild", page); // publish
236
237 if(this.persist){
238 cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
239 }
240 }
241
242 return d; // If child has an href, promise that fires when the child's href finishes loading
243 },
244
245 _transition: function(newWidget, oldWidget /*===== , animate =====*/){
246 // summary:
247 // Hide the old widget and display the new widget.
248 // Subclasses should override this.
249 // newWidget: dijit._Widget
250 // The newly selected widget.
251 // oldWidget: dijit._Widget
252 // The previously selected widget.
253 // animate: Boolean
254 // Used by AccordionContainer to turn on/off slide effect.
255 // tags:
256 // protected extension
257 if(oldWidget){
258 this._hideChild(oldWidget);
259 }
260 var d = this._showChild(newWidget);
261
262 // Size the new widget, in case this is the first time it's being shown,
263 // or I have been resized since the last time it was shown.
264 // Note that page must be visible for resizing to work.
265 if(newWidget.resize){
266 if(this.doLayout){
267 newWidget.resize(this._containerContentBox || this._contentBox);
268 }else{
269 // the child should pick it's own size but we still need to call resize()
270 // (with no arguments) to let the widget lay itself out
271 newWidget.resize();
272 }
273 }
274
275 return d; // If child has an href, promise that fires when the child's href finishes loading
276 },
277
278 _adjacent: function(/*Boolean*/ forward){
279 // summary:
280 // Gets the next/previous child widget in this container from the current selection.
281 var children = this.getChildren();
282 var index = array.indexOf(children, this.selectedChildWidget);
283 index += forward ? 1 : children.length - 1;
284 return children[ index % children.length ]; // dijit._Widget
285 },
286
287 forward: function(){
288 // summary:
289 // Advance to next page.
290 return this.selectChild(this._adjacent(true), true);
291 },
292
293 back: function(){
294 // summary:
295 // Go back to previous page.
296 return this.selectChild(this._adjacent(false), true);
297 },
298
299 _onKeyPress: function(e){
300 topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
301 },
302
303 layout: function(){
304 // Implement _LayoutWidget.layout() virtual method.
305 var child = this.selectedChildWidget;
306 if(child && child.resize){
307 if(this.doLayout){
308 child.resize(this._containerContentBox || this._contentBox);
309 }else{
310 child.resize();
311 }
312 }
313 },
314
315 _showChild: function(/*dijit._Widget*/ page){
316 // summary:
317 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
318 // it can do any updates it needs regarding loading href's etc.
319 // returns:
320 // Promise that fires when page has finished showing, or true if there's no href
321 var children = this.getChildren();
322 page.isFirstChild = (page == children[0]);
323 page.isLastChild = (page == children[children.length-1]);
324 page._set("selected", true);
325
326 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
327
328 return (page._onShow && page._onShow()) || true;
329 },
330
331 _hideChild: function(/*dijit._Widget*/ page){
332 // summary:
333 // Hide the specified child by changing it's CSS, and call _onHide() so
334 // it's notified.
335 page._set("selected", false);
336 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
337
338 page.onHide && page.onHide();
339 },
340
341 closeChild: function(/*dijit._Widget*/ page){
342 // summary:
343 // Callback when user clicks the [X] to remove a page.
344 // If onClose() returns true then remove and destroy the child.
345 // tags:
346 // private
347 var remove = page.onClose(this, page);
348 if(remove){
349 this.removeChild(page);
350 // makes sure we can clean up executeScripts in ContentPane onUnLoad
351 page.destroyRecursive();
352 }
353 },
354
355 destroyDescendants: function(/*Boolean*/ preserveDom){
356 this._descendantsBeingDestroyed = true;
357 this.selectedChildWidget = undefined;
358 array.forEach(this.getChildren(), function(child){
359 if(!preserveDom){
360 this.removeChild(child);
361 }
362 child.destroyRecursive(preserveDom);
363 }, this);
364 this._descendantsBeingDestroyed = false;
365 }
366});
367
368});