]> git.wh0rd.org Git - tt-rss.git/blob - lib/dijit/layout/StackContainer.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dijit / layout / StackContainer.js.uncompressed.js
1 define("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 /*=====
18 var _WidgetBase = dijit._WidgetBase;
19 var _LayoutWidget = dijit.layout._LayoutWidget;
20 var 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
29 if(!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.)
39 lang.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
63 return 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 });