/* Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit.layout.AccordionContainer"] = true; dojo.provide("dijit.layout.AccordionContainer"); dojo.require("dijit._Container"); dojo.require("dijit._Templated"); dojo.require("dijit._CssStateMixin"); dojo.require("dijit.layout.StackContainer"); dojo.require("dijit.layout.ContentPane"); dojo.require("dijit.layout.AccordionPane"); //dojo.require("dijit.layout.AccordionPane "); // for back compat, remove for 2.0 // Design notes: // // An AccordionContainer is a StackContainer, but each child (typically ContentPane) // is wrapped in a _AccordionInnerContainer. This is hidden from the caller. // // The resulting markup will look like: // //
//
(one pane) //
(title bar) ...
//
(content pane)
//
//
// // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper, // which on claro has a 1px border plus a 2px bottom margin. // // During animation there are two dijtAccordionChildWrapper's shown, so we need // to compensate for that. dojo.declare( "dijit.layout.AccordionContainer", dijit.layout.StackContainer, { // summary: // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time, // and switching between panes is visualized by sliding the other panes up/down. // example: // |
// |
// |
// |
// |

This is some text

// |
// |
// duration: Integer // Amount of time (in ms) it takes to slide panes duration: dijit.defaultDuration, // buttonWidget: [const] String // The name of the widget used to display the title of each pane buttonWidget: "dijit.layout._AccordionButton", /*===== // _verticalSpace: Number // Pixels of space available for the open pane // (my content box size minus the cumulative size of all the title bars) _verticalSpace: 0, =====*/ baseClass: "dijitAccordionContainer", buildRendering: function(){ this.inherited(arguments); this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css dijit.setWaiRole(this.domNode, "tablist"); // TODO: put this in template }, startup: function(){ if(this._started){ return; } this.inherited(arguments); if(this.selectedChildWidget){ var style = this.selectedChildWidget.containerNode.style; style.display = ""; style.overflow = "auto"; this.selectedChildWidget._wrapperWidget.set("selected", true); } }, layout: function(){ // Implement _LayoutWidget.layout() virtual method. // Set the height of the open pane based on what room remains. var openPane = this.selectedChildWidget; if(!openPane){ return;} // space taken up by title, plus wrapper div (with border/margin) for open pane var wrapperDomNode = openPane._wrapperWidget.domNode, wrapperDomNodeMargin = dojo._getMarginExtents(wrapperDomNode), wrapperDomNodePadBorder = dojo._getPadBorderExtents(wrapperDomNode), wrapperContainerNode = openPane._wrapperWidget.containerNode, wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode), wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode), mySize = this._contentBox; // get cumulative height of all the unselected title bars var totalCollapsedHeight = 0; dojo.forEach(this.getChildren(), function(child){ if(child != openPane){ totalCollapsedHeight += dojo._getMarginSize(child._wrapperWidget.domNode).h; } }); this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h - openPane._buttonWidget.getTitleHeight(); // Memo size to make displayed child this._containerContentBox = { h: this._verticalSpace, w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w }; if(openPane){ openPane.resize(this._containerContentBox); } }, _setupChild: function(child){ // Overrides _LayoutWidget._setupChild(). // Put wrapper widget around the child widget, showing title child._wrapperWidget = new dijit.layout._AccordionInnerContainer({ contentWidget: child, buttonWidget: this.buttonWidget, id: child.id + "_wrapper", dir: child.dir, lang: child.lang, parent: this }); this.inherited(arguments); }, addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ if(this._started){ // Adding a child to a started Accordion is complicated because children have // wrapper widgets. Default code path (calling this.inherited()) would add // the new child inside another child's wrapper. // First add in child as a direct child of this AccordionContainer dojo.place(child.domNode, this.containerNode, insertIndex); if(!child._started){ child.startup(); } // Then stick the wrapper widget around the child widget this._setupChild(child); // Code below copied from StackContainer dojo.publish(this.id+"-addChild", [child, insertIndex]); this.layout(); if(!this.selectedChildWidget){ this.selectChild(child); } }else{ // We haven't been started yet so just add in the child widget directly, // and the wrapper will be created on startup() this.inherited(arguments); } }, removeChild: function(child){ // Overrides _LayoutWidget.removeChild(). // Destroy wrapper widget first, before StackContainer.getChildren() call. // Replace wrapper widget with true child widget (ContentPane etc.). // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper. if(child._wrapperWidget){ dojo.place(child.domNode, child._wrapperWidget.domNode, "after"); child._wrapperWidget.destroy(); delete child._wrapperWidget; } dojo.removeClass(child.domNode, "dijitHidden"); this.inherited(arguments); }, getChildren: function(){ // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes return dojo.map(this.inherited(arguments), function(child){ return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child; }, this); }, destroy: function(){ if(this._animation){ this._animation.stop(); } dojo.forEach(this.getChildren(), function(child){ // If AccordionContainer has been started, then each child has a wrapper widget which // also needs to be destroyed. if(child._wrapperWidget){ child._wrapperWidget.destroy(); }else{ child.destroyRecursive(); } }); this.inherited(arguments); }, _showChild: function(child){ // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode child._wrapperWidget.containerNode.style.display="block"; return this.inherited(arguments); }, _hideChild: function(child){ // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode child._wrapperWidget.containerNode.style.display="none"; this.inherited(arguments); }, _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){ // Overrides StackContainer._transition() to provide sliding of title bars etc. if(dojo.isIE < 8){ // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7 animate = false; } if(this._animation){ // there's an in-progress animation. speedily end it so we can do the newly requested one this._animation.stop(true); delete this._animation; } var self = this; if(newWidget){ newWidget._wrapperWidget.set("selected", true); var d = this._showChild(newWidget); // prepare widget to be slid in // Size the new widget, in case this is the first time it's being shown, // or I have been resized since the last time it was shown. // Note that page must be visible for resizing to work. if(this.doLayout && newWidget.resize){ newWidget.resize(this._containerContentBox); } } if(oldWidget){ oldWidget._wrapperWidget.set("selected", false); if(!animate){ this._hideChild(oldWidget); } } if(animate){ var newContents = newWidget._wrapperWidget.containerNode, oldContents = oldWidget._wrapperWidget.containerNode; // During the animation we will be showing two dijitAccordionChildWrapper nodes at once, // which on claro takes up 4px extra space (compared to stable AccordionContainer). // Have to compensate for that by immediately shrinking the pane being closed. var wrapperContainerNode = newWidget._wrapperWidget.containerNode, wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode), wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode), animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h; oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px"; this._animation = new dojo.Animation({ node: newContents, duration: this.duration, curve: [1, this._verticalSpace - animationHeightOverhead - 1], onAnimate: function(value){ value = Math.floor(value); // avoid fractional values newContents.style.height = value + "px"; oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px"; }, onEnd: function(){ delete self._animation; newContents.style.height = "auto"; oldWidget._wrapperWidget.containerNode.style.display = "none"; oldContents.style.height = "auto"; self._hideChild(oldWidget); } }); this._animation.onStop = this._animation.onEnd; this._animation.play(); } return d; // If child has an href, promise that fires when the widget has finished loading }, // note: we are treating the container as controller here _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){ // summary: // Handle keypress events // description: // This is called from a handler on AccordionContainer.domNode // (setup in StackContainer), and is also called directly from // the click handler for accordion labels if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){ return; } var k = dojo.keys, c = e.charOrCode; if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) || (e.ctrlKey && c == k.PAGE_UP)){ this._adjacent(false)._buttonWidget._onTitleClick(); dojo.stopEvent(e); }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) || (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){ this._adjacent(true)._buttonWidget._onTitleClick(); dojo.stopEvent(e); } } } ); dojo.declare("dijit.layout._AccordionInnerContainer", [dijit._Widget, dijit._CssStateMixin], { // summary: // Internal widget placed as direct child of AccordionContainer.containerNode. // When other widgets are added as children to an AccordionContainer they are wrapped in // this widget. /*===== // buttonWidget: String // Name of class to use to instantiate title // (Wish we didn't have a separate widget for just the title but maintaining it // for backwards compatibility, is it worth it?) buttonWidget: null, =====*/ /*===== // contentWidget: dijit._Widget // Pointer to the real child widget contentWidget: null, =====*/ baseClass: "dijitAccordionInnerContainer", // tell nested layout widget that we will take care of sizing isContainer: true, isLayoutContainer: true, buildRendering: function(){ // Builds a template like: //
// Button //
// ContentPane //
//
// Create wrapper div, placed where the child is now this.domNode = dojo.place("
", this.contentWidget.domNode, "after"); // wrapper div's first child is the button widget (ie, the title bar) var child = this.contentWidget, cls = dojo.getObject(this.buttonWidget); this.button = child._buttonWidget = (new cls({ contentWidget: child, label: child.title, title: child.tooltip, dir: child.dir, lang: child.lang, iconClass: child.iconClass, id: child.id + "_button", parent: this.parent })).placeAt(this.domNode); // and then the actual content widget (changing it from prior-sibling to last-child), // wrapped by a
this.containerNode = dojo.place("