2 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
3 define("dijit/layout/AccordionContainer", [
5 "dojo/_base/array", // array.forEach array.map
6 "dojo/_base/declare", // declare
7 "dojo/_base/event", // event.stop
8 "dojo/_base/fx", // fx.Animation
9 "dojo/dom", // dom.setSelectable
10 "dojo/dom-attr", // domAttr.attr
11 "dojo/dom-class", // domClass.remove
12 "dojo/dom-construct", // domConstruct.place
16 "dojo/_base/lang", // lang.getObject lang.hitch
17 "dojo/_base/sniff", // has("ie")
18 "dojo/topic", // publish
19 "../focus", // focus.focus()
20 "../_base/manager", // manager.defaultDuration
28 "dojo/text!./templates/AccordionButton.html"
29 ], function(require
, array
, declare
, event
, fx
, dom
, domAttr
, domClass
, domConstruct
, domGeometry
,
30 kernel
, keys
, lang
, has
, topic
, focus
, manager
, ready
,
31 _Widget
, _Container
, _TemplatedMixin
, _CssStateMixin
, StackContainer
, ContentPane
, template
){
34 var _Widget = dijit._Widget;
35 var _Container = dijit._Container;
36 var _TemplatedMixin = dijit._TemplatedMixin;
37 var _CssStateMixin = dijit._CssStateMixin;
38 var StackContainer = dijit.layout.StackContainer;
39 var ContentPane = dijit.layout.ContentPane;
43 // dijit/layout/AccordionContainer
45 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
46 // and switching between panes is visualized by sliding the other panes up/down.
51 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
52 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
54 // The resulting markup will look like:
56 // <div class=dijitAccordionContainer>
57 // <div class=dijitAccordionInnerContainer> (one pane)
58 // <div class=dijitAccordionTitle> (title bar) ... </div>
59 // <div class=dijtAccordionChildWrapper> (content pane) </div>
63 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
64 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
65 // which on claro has a 1px border plus a 2px bottom margin.
67 // During animation there are two dijtAccordionChildWrapper's shown, so we need
68 // to compensate for that.
71 var AccordionButton
= declare("dijit.layout._AccordionButton", [_Widget
, _TemplatedMixin
, _CssStateMixin
], {
73 // The title bar to click to open up an accordion pane.
74 // Internal widget used by AccordionContainer.
78 templateString
: template
,
83 _setLabelAttr
: {node
: "titleTextNode", type
: "innerHTML" },
86 // Tooltip that appears on hover
88 _setTitleAttr
: {node
: "titleTextNode", type
: "attribute", attribute
: "title"},
90 // iconClassAttr: String
91 // CSS class for icon to left of label
93 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
95 baseClass
: "dijitAccordionTitle",
97 getParent: function(){
99 // Returns the AccordionContainer parent.
105 buildRendering: function(){
106 this.inherited(arguments
);
107 var titleTextNodeId
= this.id
.replace(' ','_');
108 domAttr
.set(this.titleTextNode
, "id", titleTextNodeId
+"_title");
109 this.focusNode
.setAttribute("aria-labelledby", domAttr
.get(this.titleTextNode
, "id"));
110 dom
.setSelectable(this.domNode
, false);
113 getTitleHeight: function(){
115 // Returns the height of the title dom node.
116 return domGeometry
.getMarginSize(this.domNode
).h
; // Integer
119 // TODO: maybe the parent should set these methods directly rather than forcing the code
120 // into the button widget?
121 _onTitleClick: function(){
123 // Callback when someone clicks my title.
124 var parent
= this.getParent();
125 parent
.selectChild(this.contentWidget
, true);
126 focus
.focus(this.focusNode
);
129 _onTitleKeyPress: function(/*Event*/ evt
){
130 return this.getParent()._onKeyPress(evt
, this.contentWidget
);
133 _setSelectedAttr: function(/*Boolean*/ isSelected
){
134 this._set("selected", isSelected
);
135 this.focusNode
.setAttribute("aria-expanded", isSelected
);
136 this.focusNode
.setAttribute("aria-selected", isSelected
);
137 this.focusNode
.setAttribute("tabIndex", isSelected
? "0" : "-1");
141 var AccordionInnerContainer
= declare("dijit.layout._AccordionInnerContainer", [_Widget
, _CssStateMixin
], {
143 // Internal widget placed as direct child of AccordionContainer.containerNode.
144 // When other widgets are added as children to an AccordionContainer they are wrapped in
148 // buttonWidget: Function || String
149 // Class to use to instantiate title
150 // (Wish we didn't have a separate widget for just the title but maintaining it
151 // for backwards compatibility, is it worth it?)
156 // contentWidget: dijit._Widget
157 // Pointer to the real child widget
161 baseClass
: "dijitAccordionInnerContainer",
163 // tell nested layout widget that we will take care of sizing
164 isLayoutContainer
: true,
166 buildRendering: function(){
167 // Builds a template like:
168 // <div class=dijitAccordionInnerContainer>
170 // <div class=dijitAccordionChildWrapper>
175 // Create wrapper div, placed where the child is now
176 this.domNode
= domConstruct
.place("<div class='" + this.baseClass
+
177 "' role='presentation'>", this.contentWidget
.domNode
, "after");
179 // wrapper div's first child is the button widget (ie, the title bar)
180 var child
= this.contentWidget
,
181 cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
182 this.button
= child
._buttonWidget
= (new cls({
183 contentWidget
: child
,
185 title
: child
.tooltip
,
188 textDir
: child
.textDir
,
189 iconClass
: child
.iconClass
,
190 id
: child
.id
+ "_button",
192 })).placeAt(this.domNode
);
194 // and then the actual content widget (changing it from prior-sibling to last-child),
195 // wrapped by a <div class=dijitAccordionChildWrapper>
196 this.containerNode
= domConstruct
.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode
);
197 domConstruct
.place(this.contentWidget
.domNode
, this.containerNode
);
200 postCreate: function(){
201 this.inherited(arguments
);
203 // Map changes in content widget's title etc. to changes in the button
204 var button
= this.button
;
205 this._contentWidgetWatches
= [
206 this.contentWidget
.watch('title', lang
.hitch(this, function(name
, oldValue
, newValue
){
207 button
.set("label", newValue
);
209 this.contentWidget
.watch('tooltip', lang
.hitch(this, function(name
, oldValue
, newValue
){
210 button
.set("title", newValue
);
212 this.contentWidget
.watch('iconClass', lang
.hitch(this, function(name
, oldValue
, newValue
){
213 button
.set("iconClass", newValue
);
218 _setSelectedAttr: function(/*Boolean*/ isSelected
){
219 this._set("selected", isSelected
);
220 this.button
.set("selected", isSelected
);
222 var cw
= this.contentWidget
;
223 if(cw
.onSelected
){ cw
.onSelected(); }
228 // Called by _Container.addChild()
229 this.contentWidget
.startup();
233 this.button
.destroyRecursive();
235 array
.forEach(this._contentWidgetWatches
|| [], function(w
){ w
.unwatch(); });
237 delete this.contentWidget
._buttonWidget
;
238 delete this.contentWidget
._wrapperWidget
;
240 this.inherited(arguments
);
243 destroyDescendants: function(/*Boolean*/ preserveDom
){
244 // since getChildren isn't working for me, have to code this manually
245 this.contentWidget
.destroyRecursive(preserveDom
);
249 var AccordionContainer
= declare("dijit.layout.AccordionContainer", StackContainer
, {
251 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
252 // and switching between panes is visualized by sliding the other panes up/down.
254 // | <div data-dojo-type="dijit.layout.AccordionContainer">
255 // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 1">
257 // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 2">
258 // | <p>This is some text</p>
263 // Amount of time (in ms) it takes to slide panes
264 duration
: manager
.defaultDuration
,
266 // buttonWidget: [const] String
267 // The name of the widget used to display the title of each pane
268 buttonWidget
: AccordionButton
,
271 // _verticalSpace: Number
272 // Pixels of space available for the open pane
273 // (my content box size minus the cumulative size of all the title bars)
276 baseClass
: "dijitAccordionContainer",
278 buildRendering: function(){
279 this.inherited(arguments
);
280 this.domNode
.style
.overflow
= "hidden"; // TODO: put this in dijit.css
281 this.domNode
.setAttribute("role", "tablist"); // TODO: put this in template
285 if(this._started
){ return; }
286 this.inherited(arguments
);
287 if(this.selectedChildWidget
){
288 var style
= this.selectedChildWidget
.containerNode
.style
;
290 style
.overflow
= "auto";
291 this.selectedChildWidget
._wrapperWidget
.set("selected", true);
296 // Implement _LayoutWidget.layout() virtual method.
297 // Set the height of the open pane based on what room remains.
299 var openPane
= this.selectedChildWidget
;
301 if(!openPane
){ return;}
303 // space taken up by title, plus wrapper div (with border/margin) for open pane
304 var wrapperDomNode
= openPane
._wrapperWidget
.domNode
,
305 wrapperDomNodeMargin
= domGeometry
.getMarginExtents(wrapperDomNode
),
306 wrapperDomNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperDomNode
),
307 wrapperContainerNode
= openPane
._wrapperWidget
.containerNode
,
308 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
309 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
310 mySize
= this._contentBox
;
312 // get cumulative height of all the unselected title bars
313 var totalCollapsedHeight
= 0;
314 array
.forEach(this.getChildren(), function(child
){
315 if(child
!= openPane
){
316 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
317 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
318 // margin below the bottom pane (even though we don't want one).
319 totalCollapsedHeight
+= domGeometry
.getMarginSize(child
._wrapperWidget
.domNode
).h
;
322 this._verticalSpace
= mySize
.h
- totalCollapsedHeight
- wrapperDomNodeMargin
.h
323 - wrapperDomNodePadBorder
.h
- wrapperContainerNodeMargin
.h
- wrapperContainerNodePadBorder
.h
324 - openPane
._buttonWidget
.getTitleHeight();
326 // Memo size to make displayed child
327 this._containerContentBox
= {
328 h
: this._verticalSpace
,
329 w
: this._contentBox
.w
- wrapperDomNodeMargin
.w
- wrapperDomNodePadBorder
.w
330 - wrapperContainerNodeMargin
.w
- wrapperContainerNodePadBorder
.w
334 openPane
.resize(this._containerContentBox
);
338 _setupChild: function(child
){
339 // Overrides _LayoutWidget._setupChild().
340 // Put wrapper widget around the child widget, showing title
342 child
._wrapperWidget
= AccordionInnerContainer({
343 contentWidget
: child
,
344 buttonWidget
: this.buttonWidget
,
345 id
: child
.id
+ "_wrapper",
348 textDir
: child
.textDir
,
352 this.inherited(arguments
);
355 addChild: function(/*dijit._Widget*/ child
, /*Integer?*/ insertIndex
){
357 // Adding a child to a started Accordion is complicated because children have
358 // wrapper widgets. Default code path (calling this.inherited()) would add
359 // the new child inside another child's wrapper.
361 // First add in child as a direct child of this AccordionContainer
362 var refNode
= this.containerNode
;
363 if(insertIndex
&& typeof insertIndex
== "number"){
364 var children
= _Widget
.prototype.getChildren
.call(this); // get wrapper panes
365 if(children
&& children
.length
>= insertIndex
){
366 refNode
= children
[insertIndex
-1].domNode
;
367 insertIndex
= "after";
370 domConstruct
.place(child
.domNode
, refNode
, insertIndex
);
376 // Then stick the wrapper widget around the child widget
377 this._setupChild(child
);
379 // Code below copied from StackContainer
380 topic
.publish(this.id
+"-addChild", child
, insertIndex
); // publish
382 if(!this.selectedChildWidget
){
383 this.selectChild(child
);
386 // We haven't been started yet so just add in the child widget directly,
387 // and the wrapper will be created on startup()
388 this.inherited(arguments
);
392 removeChild: function(child
){
393 // Overrides _LayoutWidget.removeChild().
395 // Destroy wrapper widget first, before StackContainer.getChildren() call.
396 // Replace wrapper widget with true child widget (ContentPane etc.).
397 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
398 if(child
._wrapperWidget
){
399 domConstruct
.place(child
.domNode
, child
._wrapperWidget
.domNode
, "after");
400 child
._wrapperWidget
.destroy();
401 delete child
._wrapperWidget
;
404 domClass
.remove(child
.domNode
, "dijitHidden");
406 this.inherited(arguments
);
409 getChildren: function(){
410 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
411 return array
.map(this.inherited(arguments
), function(child
){
412 return child
.declaredClass
== "dijit.layout._AccordionInnerContainer" ? child
.contentWidget
: child
;
418 this._animation
.stop();
420 array
.forEach(this.getChildren(), function(child
){
421 // If AccordionContainer has been started, then each child has a wrapper widget which
422 // also needs to be destroyed.
423 if(child
._wrapperWidget
){
424 child
._wrapperWidget
.destroy();
426 child
.destroyRecursive();
429 this.inherited(arguments
);
432 _showChild: function(child
){
433 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
434 child
._wrapperWidget
.containerNode
.style
.display
="block";
435 return this.inherited(arguments
);
438 _hideChild: function(child
){
439 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
440 child
._wrapperWidget
.containerNode
.style
.display
="none";
441 this.inherited(arguments
);
444 _transition: function(/*dijit._Widget?*/ newWidget
, /*dijit._Widget?*/ oldWidget
, /*Boolean*/ animate
){
445 // Overrides StackContainer._transition() to provide sliding of title bars etc.
448 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
453 // there's an in-progress animation. speedily end it so we can do the newly requested one
454 this._animation
.stop(true);
455 delete this._animation
;
461 newWidget
._wrapperWidget
.set("selected", true);
463 var d
= this._showChild(newWidget
); // prepare widget to be slid in
465 // Size the new widget, in case this is the first time it's being shown,
466 // or I have been resized since the last time it was shown.
467 // Note that page must be visible for resizing to work.
468 if(this.doLayout
&& newWidget
.resize
){
469 newWidget
.resize(this._containerContentBox
);
474 oldWidget
._wrapperWidget
.set("selected", false);
476 this._hideChild(oldWidget
);
481 var newContents
= newWidget
._wrapperWidget
.containerNode
,
482 oldContents
= oldWidget
._wrapperWidget
.containerNode
;
484 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
485 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
486 // Have to compensate for that by immediately shrinking the pane being closed.
487 var wrapperContainerNode
= newWidget
._wrapperWidget
.containerNode
,
488 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
489 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
490 animationHeightOverhead
= wrapperContainerNodeMargin
.h
+ wrapperContainerNodePadBorder
.h
;
492 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
) + "px";
494 this._animation
= new fx
.Animation({
496 duration
: this.duration
,
497 curve
: [1, this._verticalSpace
- animationHeightOverhead
- 1],
498 onAnimate: function(value
){
499 value
= Math
.floor(value
); // avoid fractional values
500 newContents
.style
.height
= value
+ "px";
501 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
- value
) + "px";
504 delete self
._animation
;
505 newContents
.style
.height
= "auto";
506 oldWidget
._wrapperWidget
.containerNode
.style
.display
= "none";
507 oldContents
.style
.height
= "auto";
508 self
._hideChild(oldWidget
);
511 this._animation
.onStop
= this._animation
.onEnd
;
512 this._animation
.play();
515 return d
; // If child has an href, promise that fires when the widget has finished loading
518 // note: we are treating the container as controller here
519 _onKeyPress: function(/*Event*/ e
, /*dijit._Widget*/ fromTitle
){
521 // Handle keypress events
523 // This is called from a handler on AccordionContainer.domNode
524 // (setup in StackContainer), and is also called directly from
525 // the click handler for accordion labels
526 if(this.disabled
|| e
.altKey
|| !(fromTitle
|| e
.ctrlKey
)){
529 var c
= e
.charOrCode
;
530 if((fromTitle
&& (c
== keys
.LEFT_ARROW
|| c
== keys
.UP_ARROW
)) ||
531 (e
.ctrlKey
&& c
== keys
.PAGE_UP
)){
532 this._adjacent(false)._buttonWidget
._onTitleClick();
534 }else if((fromTitle
&& (c
== keys
.RIGHT_ARROW
|| c
== keys
.DOWN_ARROW
)) ||
535 (e
.ctrlKey
&& (c
== keys
.PAGE_DOWN
|| c
== keys
.TAB
))){
536 this._adjacent(true)._buttonWidget
._onTitleClick();
542 // Back compat w/1.6, remove for 2.0
545 var requires
= ["dijit/layout/AccordionPane"];
546 require(requires
); // use indirection so modules not rolled into a build
550 // For monkey patching
551 AccordionContainer
._InnerContainer
= AccordionInnerContainer
;
552 AccordionContainer
._Button
= AccordionButton
;
554 return AccordionContainer
;