diff options
Diffstat (limited to 'lib/dijit/layout')
37 files changed, 4835 insertions, 4482 deletions
diff --git a/lib/dijit/layout/AccordionContainer.js b/lib/dijit/layout/AccordionContainer.js index 6177583ba..3470e5426 100644 --- a/lib/dijit/layout/AccordionContainer.js +++ b/lib/dijit/layout/AccordionContainer.js @@ -1,498 +1,2 @@ -/* - 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: -// -// <div class=dijitAccordionContainer> -// <div class=dijitAccordionInnerContainer> (one pane) -// <div class=dijitAccordionTitle> (title bar) ... </div> -// <div class=dijtAccordionChildWrapper> (content pane) </div> -// </div> -// </div> -// -// 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: - // | <div dojoType="dijit.layout.AccordionContainer"> - // | <div dojoType="dijit.layout.ContentPane" title="pane 1"> - // | </div> - // | <div dojoType="dijit.layout.ContentPane" title="pane 2"> - // | <p>This is some text</p> - // | </div> - // | </div> - - // 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: - // <div class=dijitAccordionInnerContainer> - // Button - // <div class=dijitAccordionChildWrapper> - // ContentPane - // </div> - // </div> - - // Create wrapper div, placed where the child is now - this.domNode = dojo.place("<div class='" + this.baseClass + "'>", 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 <div class=dijitAccordionChildWrapper> - this.containerNode = dojo.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode); - dojo.place(this.contentWidget.domNode, this.containerNode); - }, - - postCreate: function(){ - this.inherited(arguments); - - // Map changes in content widget's title etc. to changes in the button - var button = this.button; - this._contentWidgetWatches = [ - this.contentWidget.watch('title', dojo.hitch(this, function(name, oldValue, newValue){ - button.set("label", newValue); - })), - this.contentWidget.watch('tooltip', dojo.hitch(this, function(name, oldValue, newValue){ - button.set("title", newValue); - })), - this.contentWidget.watch('iconClass', dojo.hitch(this, function(name, oldValue, newValue){ - button.set("iconClass", newValue); - })) - ]; - }, - - _setSelectedAttr: function(/*Boolean*/ isSelected){ - this._set("selected", isSelected); - this.button.set("selected", isSelected); - if(isSelected){ - var cw = this.contentWidget; - if(cw.onSelected){ cw.onSelected(); } - } - }, - - startup: function(){ - // Called by _Container.addChild() - this.contentWidget.startup(); - }, - - destroy: function(){ - this.button.destroyRecursive(); - - dojo.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); }); - - delete this.contentWidget._buttonWidget; - delete this.contentWidget._wrapperWidget; - - this.inherited(arguments); - }, - - destroyDescendants: function(){ - // since getChildren isn't working for me, have to code this manually - this.contentWidget.destroyRecursive(); - } -}); - -dojo.declare("dijit.layout._AccordionButton", - [dijit._Widget, dijit._Templated, dijit._CssStateMixin], - { - // summary: - // The title bar to click to open up an accordion pane. - // Internal widget used by AccordionContainer. - // tags: - // private - - templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "<div dojoAttachEvent='onclick:_onTitleClick' class='dijitAccordionTitle'>\n\t<div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='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\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"), - attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), { - label: {node: "titleTextNode", type: "innerHTML" }, - title: {node: "titleTextNode", type: "attribute", attribute: "title"}, - iconClass: { node: "iconNode", type: "class" } - }), - - baseClass: "dijitAccordionTitle", - - getParent: function(){ - // summary: - // Returns the AccordionContainer parent. - // tags: - // private - return this.parent; - }, - - buildRendering: function(){ - this.inherited(arguments); - var titleTextNodeId = this.id.replace(' ','_'); - dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title"); - dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id")); - dojo.setSelectable(this.domNode, false); - }, - - getTitleHeight: function(){ - // summary: - // Returns the height of the title dom node. - return dojo._getMarginSize(this.domNode).h; // Integer - }, - - // TODO: maybe the parent should set these methods directly rather than forcing the code - // into the button widget? - _onTitleClick: function(){ - // summary: - // Callback when someone clicks my title. - var parent = this.getParent(); - parent.selectChild(this.contentWidget, true); - dijit.focus(this.focusNode); - }, - - _onTitleKeyPress: function(/*Event*/ evt){ - return this.getParent()._onKeyPress(evt, this.contentWidget); - }, - - _setSelectedAttr: function(/*Boolean*/ isSelected){ - this._set("selected", isSelected); - dijit.setWaiState(this.focusNode, "expanded", isSelected); - dijit.setWaiState(this.focusNode, "selected", isSelected); - this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1"); - } -}); - -} +//>>built +require({cache:{"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"}});define("dijit/layout/AccordionContainer",["require","dojo/_base/array","dojo/_base/declare","dojo/_base/event","dojo/_base/fx","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/dom-construct","dojo/dom-geometry","dojo/_base/kernel","dojo/keys","dojo/_base/lang","dojo/_base/sniff","dojo/topic","../focus","../_base/manager","dojo/ready","../_Widget","../_Container","../_TemplatedMixin","../_CssStateMixin","./StackContainer","./ContentPane","dojo/text!./templates/AccordionButton.html"],function(_1,_2,_3,_4,fx,_5,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f,_10,_11,_12,_13,_14,_15,_16,_17,_18){var _19=_3("dijit.layout._AccordionButton",[_12,_14,_15],{templateString:_18,label:"",_setLabelAttr:{node:"titleTextNode",type:"innerHTML"},title:"",_setTitleAttr:{node:"titleTextNode",type:"attribute",attribute:"title"},iconClassAttr:"",_setIconClassAttr:{node:"iconNode",type:"class"},baseClass:"dijitAccordionTitle",getParent:function(){return this.parent;},buildRendering:function(){this.inherited(arguments);var _1a=this.id.replace(" ","_");_6.set(this.titleTextNode,"id",_1a+"_title");this.focusNode.setAttribute("aria-labelledby",_6.get(this.titleTextNode,"id"));_5.setSelectable(this.domNode,false);},getTitleHeight:function(){return _9.getMarginSize(this.domNode).h;},_onTitleClick:function(){var _1b=this.getParent();_1b.selectChild(this.contentWidget,true);_f.focus(this.focusNode);},_onTitleKeyPress:function(evt){return this.getParent()._onKeyPress(evt,this.contentWidget);},_setSelectedAttr:function(_1c){this._set("selected",_1c);this.focusNode.setAttribute("aria-expanded",_1c);this.focusNode.setAttribute("aria-selected",_1c);this.focusNode.setAttribute("tabIndex",_1c?"0":"-1");}});var _1d=_3("dijit.layout._AccordionInnerContainer",[_12,_15],{baseClass:"dijitAccordionInnerContainer",isLayoutContainer:true,buildRendering:function(){this.domNode=_8.place("<div class='"+this.baseClass+"' role='presentation'>",this.contentWidget.domNode,"after");var _1e=this.contentWidget,cls=_c.isString(this.buttonWidget)?_c.getObject(this.buttonWidget):this.buttonWidget;this.button=_1e._buttonWidget=(new cls({contentWidget:_1e,label:_1e.title,title:_1e.tooltip,dir:_1e.dir,lang:_1e.lang,textDir:_1e.textDir,iconClass:_1e.iconClass,id:_1e.id+"_button",parent:this.parent})).placeAt(this.domNode);this.containerNode=_8.place("<div class='dijitAccordionChildWrapper' style='display:none'>",this.domNode);_8.place(this.contentWidget.domNode,this.containerNode);},postCreate:function(){this.inherited(arguments);var _1f=this.button;this._contentWidgetWatches=[this.contentWidget.watch("title",_c.hitch(this,function(_20,_21,_22){_1f.set("label",_22);})),this.contentWidget.watch("tooltip",_c.hitch(this,function(_23,_24,_25){_1f.set("title",_25);})),this.contentWidget.watch("iconClass",_c.hitch(this,function(_26,_27,_28){_1f.set("iconClass",_28);}))];},_setSelectedAttr:function(_29){this._set("selected",_29);this.button.set("selected",_29);if(_29){var cw=this.contentWidget;if(cw.onSelected){cw.onSelected();}}},startup:function(){this.contentWidget.startup();},destroy:function(){this.button.destroyRecursive();_2.forEach(this._contentWidgetWatches||[],function(w){w.unwatch();});delete this.contentWidget._buttonWidget;delete this.contentWidget._wrapperWidget;this.inherited(arguments);},destroyDescendants:function(_2a){this.contentWidget.destroyRecursive(_2a);}});var _2b=_3("dijit.layout.AccordionContainer",_16,{duration:_10.defaultDuration,buttonWidget:_19,baseClass:"dijitAccordionContainer",buildRendering:function(){this.inherited(arguments);this.domNode.style.overflow="hidden";this.domNode.setAttribute("role","tablist");},startup:function(){if(this._started){return;}this.inherited(arguments);if(this.selectedChildWidget){var _2c=this.selectedChildWidget.containerNode.style;_2c.display="";_2c.overflow="auto";this.selectedChildWidget._wrapperWidget.set("selected",true);}},layout:function(){var _2d=this.selectedChildWidget;if(!_2d){return;}var _2e=_2d._wrapperWidget.domNode,_2f=_9.getMarginExtents(_2e),_30=_9.getPadBorderExtents(_2e),_31=_2d._wrapperWidget.containerNode,_32=_9.getMarginExtents(_31),_33=_9.getPadBorderExtents(_31),_34=this._contentBox;var _35=0;_2.forEach(this.getChildren(),function(_36){if(_36!=_2d){_35+=_9.getMarginSize(_36._wrapperWidget.domNode).h;}});this._verticalSpace=_34.h-_35-_2f.h-_30.h-_32.h-_33.h-_2d._buttonWidget.getTitleHeight();this._containerContentBox={h:this._verticalSpace,w:this._contentBox.w-_2f.w-_30.w-_32.w-_33.w};if(_2d){_2d.resize(this._containerContentBox);}},_setupChild:function(_37){_37._wrapperWidget=_1d({contentWidget:_37,buttonWidget:this.buttonWidget,id:_37.id+"_wrapper",dir:_37.dir,lang:_37.lang,textDir:_37.textDir,parent:this});this.inherited(arguments);},addChild:function(_38,_39){if(this._started){var _3a=this.containerNode;if(_39&&typeof _39=="number"){var _3b=_12.prototype.getChildren.call(this);if(_3b&&_3b.length>=_39){_3a=_3b[_39-1].domNode;_39="after";}}_8.place(_38.domNode,_3a,_39);if(!_38._started){_38.startup();}this._setupChild(_38);_e.publish(this.id+"-addChild",_38,_39);this.layout();if(!this.selectedChildWidget){this.selectChild(_38);}}else{this.inherited(arguments);}},removeChild:function(_3c){if(_3c._wrapperWidget){_8.place(_3c.domNode,_3c._wrapperWidget.domNode,"after");_3c._wrapperWidget.destroy();delete _3c._wrapperWidget;}_7.remove(_3c.domNode,"dijitHidden");this.inherited(arguments);},getChildren:function(){return _2.map(this.inherited(arguments),function(_3d){return _3d.declaredClass=="dijit.layout._AccordionInnerContainer"?_3d.contentWidget:_3d;},this);},destroy:function(){if(this._animation){this._animation.stop();}_2.forEach(this.getChildren(),function(_3e){if(_3e._wrapperWidget){_3e._wrapperWidget.destroy();}else{_3e.destroyRecursive();}});this.inherited(arguments);},_showChild:function(_3f){_3f._wrapperWidget.containerNode.style.display="block";return this.inherited(arguments);},_hideChild:function(_40){_40._wrapperWidget.containerNode.style.display="none";this.inherited(arguments);},_transition:function(_41,_42,_43){if(_d("ie")<8){_43=false;}if(this._animation){this._animation.stop(true);delete this._animation;}var _44=this;if(_41){_41._wrapperWidget.set("selected",true);var d=this._showChild(_41);if(this.doLayout&&_41.resize){_41.resize(this._containerContentBox);}}if(_42){_42._wrapperWidget.set("selected",false);if(!_43){this._hideChild(_42);}}if(_43){var _45=_41._wrapperWidget.containerNode,_46=_42._wrapperWidget.containerNode;var _47=_41._wrapperWidget.containerNode,_48=_9.getMarginExtents(_47),_49=_9.getPadBorderExtents(_47),_4a=_48.h+_49.h;_46.style.height=(_44._verticalSpace-_4a)+"px";this._animation=new fx.Animation({node:_45,duration:this.duration,curve:[1,this._verticalSpace-_4a-1],onAnimate:function(_4b){_4b=Math.floor(_4b);_45.style.height=_4b+"px";_46.style.height=(_44._verticalSpace-_4a-_4b)+"px";},onEnd:function(){delete _44._animation;_45.style.height="auto";_42._wrapperWidget.containerNode.style.display="none";_46.style.height="auto";_44._hideChild(_42);}});this._animation.onStop=this._animation.onEnd;this._animation.play();}return d;},_onKeyPress:function(e,_4c){if(this.disabled||e.altKey||!(_4c||e.ctrlKey)){return;}var c=e.charOrCode;if((_4c&&(c==_b.LEFT_ARROW||c==_b.UP_ARROW))||(e.ctrlKey&&c==_b.PAGE_UP)){this._adjacent(false)._buttonWidget._onTitleClick();_4.stop(e);}else{if((_4c&&(c==_b.RIGHT_ARROW||c==_b.DOWN_ARROW))||(e.ctrlKey&&(c==_b.PAGE_DOWN||c==_b.TAB))){this._adjacent(true)._buttonWidget._onTitleClick();_4.stop(e);}}}});if(!_a.isAsync){_11(0,function(){var _4d=["dijit/layout/AccordionPane"];_1(_4d);});}_2b._InnerContainer=_1d;_2b._Button=_19;return _2b;});
\ No newline at end of file diff --git a/lib/dijit/layout/AccordionContainer.js.uncompressed.js b/lib/dijit/layout/AccordionContainer.js.uncompressed.js new file mode 100644 index 000000000..a4da71b3d --- /dev/null +++ b/lib/dijit/layout/AccordionContainer.js.uncompressed.js @@ -0,0 +1,555 @@ +require({cache:{ +'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"}}); +define("dijit/layout/AccordionContainer", [ + "require", + "dojo/_base/array", // array.forEach array.map + "dojo/_base/declare", // declare + "dojo/_base/event", // event.stop + "dojo/_base/fx", // fx.Animation + "dojo/dom", // dom.setSelectable + "dojo/dom-attr", // domAttr.attr + "dojo/dom-class", // domClass.remove + "dojo/dom-construct", // domConstruct.place + "dojo/dom-geometry", + "dojo/_base/kernel", + "dojo/keys", // keys + "dojo/_base/lang", // lang.getObject lang.hitch + "dojo/_base/sniff", // has("ie") + "dojo/topic", // publish + "../focus", // focus.focus() + "../_base/manager", // manager.defaultDuration + "dojo/ready", + "../_Widget", + "../_Container", + "../_TemplatedMixin", + "../_CssStateMixin", + "./StackContainer", + "./ContentPane", + "dojo/text!./templates/AccordionButton.html" +], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry, + kernel, keys, lang, has, topic, focus, manager, ready, + _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){ + +/*===== + var _Widget = dijit._Widget; + var _Container = dijit._Container; + var _TemplatedMixin = dijit._TemplatedMixin; + var _CssStateMixin = dijit._CssStateMixin; + var StackContainer = dijit.layout.StackContainer; + var ContentPane = dijit.layout.ContentPane; +=====*/ + + // module: + // dijit/layout/AccordionContainer + // 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. + + + // 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: + // + // <div class=dijitAccordionContainer> + // <div class=dijitAccordionInnerContainer> (one pane) + // <div class=dijitAccordionTitle> (title bar) ... </div> + // <div class=dijtAccordionChildWrapper> (content pane) </div> + // </div> + // </div> + // + // 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. + + + var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], { + // summary: + // The title bar to click to open up an accordion pane. + // Internal widget used by AccordionContainer. + // tags: + // private + + templateString: template, + + // label: String + // Title of the pane + label: "", + _setLabelAttr: {node: "titleTextNode", type: "innerHTML" }, + + // title: String + // Tooltip that appears on hover + title: "", + _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"}, + + // iconClassAttr: String + // CSS class for icon to left of label + iconClassAttr: "", + _setIconClassAttr: { node: "iconNode", type: "class" }, + + baseClass: "dijitAccordionTitle", + + getParent: function(){ + // summary: + // Returns the AccordionContainer parent. + // tags: + // private + return this.parent; + }, + + buildRendering: function(){ + this.inherited(arguments); + var titleTextNodeId = this.id.replace(' ','_'); + domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title"); + this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id")); + dom.setSelectable(this.domNode, false); + }, + + getTitleHeight: function(){ + // summary: + // Returns the height of the title dom node. + return domGeometry.getMarginSize(this.domNode).h; // Integer + }, + + // TODO: maybe the parent should set these methods directly rather than forcing the code + // into the button widget? + _onTitleClick: function(){ + // summary: + // Callback when someone clicks my title. + var parent = this.getParent(); + parent.selectChild(this.contentWidget, true); + focus.focus(this.focusNode); + }, + + _onTitleKeyPress: function(/*Event*/ evt){ + return this.getParent()._onKeyPress(evt, this.contentWidget); + }, + + _setSelectedAttr: function(/*Boolean*/ isSelected){ + this._set("selected", isSelected); + this.focusNode.setAttribute("aria-expanded", isSelected); + this.focusNode.setAttribute("aria-selected", isSelected); + this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1"); + } + }); + + var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _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: Function || String + // 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 + isLayoutContainer: true, + + buildRendering: function(){ + // Builds a template like: + // <div class=dijitAccordionInnerContainer> + // Button + // <div class=dijitAccordionChildWrapper> + // ContentPane + // </div> + // </div> + + // Create wrapper div, placed where the child is now + this.domNode = domConstruct.place("<div class='" + this.baseClass + + "' role='presentation'>", this.contentWidget.domNode, "after"); + + // wrapper div's first child is the button widget (ie, the title bar) + var child = this.contentWidget, + cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget; + this.button = child._buttonWidget = (new cls({ + contentWidget: child, + label: child.title, + title: child.tooltip, + dir: child.dir, + lang: child.lang, + textDir: child.textDir, + 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 <div class=dijitAccordionChildWrapper> + this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode); + domConstruct.place(this.contentWidget.domNode, this.containerNode); + }, + + postCreate: function(){ + this.inherited(arguments); + + // Map changes in content widget's title etc. to changes in the button + var button = this.button; + this._contentWidgetWatches = [ + this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){ + button.set("label", newValue); + })), + this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){ + button.set("title", newValue); + })), + this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){ + button.set("iconClass", newValue); + })) + ]; + }, + + _setSelectedAttr: function(/*Boolean*/ isSelected){ + this._set("selected", isSelected); + this.button.set("selected", isSelected); + if(isSelected){ + var cw = this.contentWidget; + if(cw.onSelected){ cw.onSelected(); } + } + }, + + startup: function(){ + // Called by _Container.addChild() + this.contentWidget.startup(); + }, + + destroy: function(){ + this.button.destroyRecursive(); + + array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); }); + + delete this.contentWidget._buttonWidget; + delete this.contentWidget._wrapperWidget; + + this.inherited(arguments); + }, + + destroyDescendants: function(/*Boolean*/ preserveDom){ + // since getChildren isn't working for me, have to code this manually + this.contentWidget.destroyRecursive(preserveDom); + } + }); + + var AccordionContainer = declare("dijit.layout.AccordionContainer", 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: + // | <div data-dojo-type="dijit.layout.AccordionContainer"> + // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 1"> + // | </div> + // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 2"> + // | <p>This is some text</p> + // | </div> + // | </div> + + // duration: Integer + // Amount of time (in ms) it takes to slide panes + duration: manager.defaultDuration, + + // buttonWidget: [const] String + // The name of the widget used to display the title of each pane + buttonWidget: 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 + this.domNode.setAttribute("role", "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 = domGeometry.getMarginExtents(wrapperDomNode), + wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode), + wrapperContainerNode = openPane._wrapperWidget.containerNode, + wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode), + wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode), + mySize = this._contentBox; + + // get cumulative height of all the unselected title bars + var totalCollapsedHeight = 0; + array.forEach(this.getChildren(), function(child){ + if(child != openPane){ + // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin + // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px + // margin below the bottom pane (even though we don't want one). + totalCollapsedHeight += domGeometry.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 = AccordionInnerContainer({ + contentWidget: child, + buttonWidget: this.buttonWidget, + id: child.id + "_wrapper", + dir: child.dir, + lang: child.lang, + textDir: child.textDir, + 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 + var refNode = this.containerNode; + if(insertIndex && typeof insertIndex == "number"){ + var children = _Widget.prototype.getChildren.call(this); // get wrapper panes + if(children && children.length >= insertIndex){ + refNode = children[insertIndex-1].domNode; + insertIndex = "after"; + } + } + domConstruct.place(child.domNode, refNode, insertIndex); + + if(!child._started){ + child.startup(); + } + + // Then stick the wrapper widget around the child widget + this._setupChild(child); + + // Code below copied from StackContainer + topic.publish(this.id+"-addChild", child, insertIndex); // publish + 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){ + domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after"); + child._wrapperWidget.destroy(); + delete child._wrapperWidget; + } + + domClass.remove(child.domNode, "dijitHidden"); + + this.inherited(arguments); + }, + + getChildren: function(){ + // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes + return array.map(this.inherited(arguments), function(child){ + return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child; + }, this); + }, + + destroy: function(){ + if(this._animation){ + this._animation.stop(); + } + array.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(has("ie") < 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 = domGeometry.getMarginExtents(wrapperContainerNode), + wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode), + animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h; + + oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px"; + + this._animation = new fx.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 c = e.charOrCode; + if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) || + (e.ctrlKey && c == keys.PAGE_UP)){ + this._adjacent(false)._buttonWidget._onTitleClick(); + event.stop(e); + }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) || + (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){ + this._adjacent(true)._buttonWidget._onTitleClick(); + event.stop(e); + } + } + }); + + // Back compat w/1.6, remove for 2.0 + if(!kernel.isAsync){ + ready(0, function(){ + var requires = ["dijit/layout/AccordionPane"]; + require(requires); // use indirection so modules not rolled into a build + }); + } + + // For monkey patching + AccordionContainer._InnerContainer = AccordionInnerContainer; + AccordionContainer._Button = AccordionButton; + + return AccordionContainer; +}); diff --git a/lib/dijit/layout/AccordionPane.js b/lib/dijit/layout/AccordionPane.js index 394ba346c..07608671f 100644 --- a/lib/dijit/layout/AccordionPane.js +++ b/lib/dijit/layout/AccordionPane.js @@ -1,30 +1,2 @@ -/* - 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.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.AccordionPane"] = true; -dojo.provide("dijit.layout.AccordionPane"); -dojo.require("dijit.layout.ContentPane"); - - -dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, { - // summary: - // Deprecated widget. Use `dijit.layout.ContentPane` instead. - // tags: - // deprecated - - constructor: function(){ - dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0"); - }, - - onSelected: function(){ - // summary: - // called when this pane is selected - } -}); - -} +//>>built +define("dijit/layout/AccordionPane",["dojo/_base/declare","dojo/_base/kernel","./ContentPane"],function(_1,_2,_3){return _1("dijit.layout.AccordionPane",_3,{constructor:function(){_2.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead","","2.0");},onSelected:function(){}});});
\ No newline at end of file diff --git a/lib/dijit/layout/AccordionPane.js.uncompressed.js b/lib/dijit/layout/AccordionPane.js.uncompressed.js new file mode 100644 index 000000000..4459329f8 --- /dev/null +++ b/lib/dijit/layout/AccordionPane.js.uncompressed.js @@ -0,0 +1,31 @@ +define("dijit/layout/AccordionPane", [ + "dojo/_base/declare", // declare + "dojo/_base/kernel", // kernel.deprecated + "./ContentPane" +], function(declare, kernel, ContentPane){ + +/*===== + var ContentPane = dijit.layout.ContentPane; +=====*/ + + // module: + // dijit/layout/AccordionPane + // summary: + // Deprecated widget. Use `dijit.layout.ContentPane` instead. + + return declare("dijit.layout.AccordionPane", ContentPane, { + // summary: + // Deprecated widget. Use `dijit.layout.ContentPane` instead. + // tags: + // deprecated + + constructor: function(){ + kernel.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0"); + }, + + onSelected: function(){ + // summary: + // called when this pane is selected + } + }); +}); diff --git a/lib/dijit/layout/BorderContainer.js b/lib/dijit/layout/BorderContainer.js index c053256d4..db41c9cb6 100644 --- a/lib/dijit/layout/BorderContainer.js +++ b/lib/dijit/layout/BorderContainer.js @@ -1,527 +1,2 @@ -/* - 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.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.BorderContainer"] = true; -dojo.provide("dijit.layout.BorderContainer"); -dojo.require("dijit.layout._LayoutWidget"); -dojo.require("dojo.cookie"); -dojo.require("dijit._Templated"); - - -dojo.declare( - "dijit.layout.BorderContainer", - dijit.layout._LayoutWidget, -{ - // summary: - // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides. - // - // description: - // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;", - // that contains a child widget marked region="center" and optionally children widgets marked - // region equal to "top", "bottom", "leading", "trailing", "left" or "right". - // Children along the edges will be laid out according to width or height dimensions and may - // include optional splitters (splitter="true") to make them resizable by the user. The remaining - // space is designated for the center region. - // - // The outer size must be specified on the BorderContainer node. Width must be specified for the sides - // and height for the top and bottom, respectively. No dimensions should be specified on the center; - // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like - // "left" and "right" except that they will be reversed in right-to-left environments. - // - // For complex layouts, multiple children can be specified for a single region. In this case, the - // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority) - // and which child is closer to the center (high layoutPriority). layoutPriority can also be used - // instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes. - // example: - // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false" - // | style="width: 400px; height: 300px;"> - // | <div dojoType="dijit.layout.ContentPane" region="top">header text</div> - // | <div dojoType="dijit.layout.ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div> - // | <div dojoType="dijit.layout.ContentPane" region="center">client area</div> - // | </div> - - // design: String - // Which design is used for the layout: - // - "headline" (default) where the top and bottom extend - // the full width of the container - // - "sidebar" where the left and right sides extend from top to bottom. - design: "headline", - - // gutters: [const] Boolean - // Give each pane a border and margin. - // Margin determined by domNode.paddingLeft. - // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing. - gutters: true, - - // liveSplitters: [const] Boolean - // Specifies whether splitters resize as you drag (true) or only upon mouseup (false) - liveSplitters: true, - - // persist: Boolean - // Save splitter positions in a cookie. - persist: false, - - baseClass: "dijitBorderContainer", - - // _splitterClass: String - // Optional hook to override the default Splitter widget used by BorderContainer - _splitterClass: "dijit.layout._Splitter", - - postMixInProperties: function(){ - // change class name to indicate that BorderContainer is being used purely for - // layout (like LayoutContainer) rather than for pretty formatting. - if(!this.gutters){ - this.baseClass += "NoGutter"; - } - this.inherited(arguments); - }, - - startup: function(){ - if(this._started){ return; } - dojo.forEach(this.getChildren(), this._setupChild, this); - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - // Override _LayoutWidget._setupChild(). - - var region = child.region; - if(region){ - this.inherited(arguments); - - dojo.addClass(child.domNode, this.baseClass+"Pane"); - - var ltr = this.isLeftToRight(); - if(region == "leading"){ region = ltr ? "left" : "right"; } - if(region == "trailing"){ region = ltr ? "right" : "left"; } - - // Create draggable splitter for resizing pane, - // or alternately if splitter=false but BorderContainer.gutters=true then - // insert dummy div just for spacing - if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){ - var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter"); - var splitter = new _Splitter({ - id: child.id + "_splitter", - container: this, - child: child, - region: region, - live: this.liveSplitters - }); - splitter.isSplitter = true; - child._splitterWidget = splitter; - - dojo.place(splitter.domNode, child.domNode, "after"); - - // Splitters aren't added as Contained children, so we need to call startup explicitly - splitter.startup(); - } - child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc. - } - }, - - layout: function(){ - // Implement _LayoutWidget.layout() virtual method. - this._layoutChildren(); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // Override _LayoutWidget.addChild(). - this.inherited(arguments); - if(this._started){ - this.layout(); //OPT - } - }, - - removeChild: function(/*dijit._Widget*/ child){ - // Override _LayoutWidget.removeChild(). - - var region = child.region; - var splitter = child._splitterWidget - if(splitter){ - splitter.destroy(); - delete child._splitterWidget; - } - this.inherited(arguments); - - if(this._started){ - this._layoutChildren(); - } - // Clean up whatever style changes we made to the child pane. - // Unclear how height and width should be handled. - dojo.removeClass(child.domNode, this.baseClass+"Pane"); - dojo.style(child.domNode, { - top: "auto", - bottom: "auto", - left: "auto", - right: "auto", - position: "static" - }); - dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto"); - }, - - getChildren: function(){ - // Override _LayoutWidget.getChildren() to only return real children, not the splitters. - return dojo.filter(this.inherited(arguments), function(widget){ - return !widget.isSplitter; - }); - }, - - // TODO: remove in 2.0 - getSplitter: function(/*String*/region){ - // summary: - // Returns the widget responsible for rendering the splitter associated with region - // tags: - // deprecated - return dojo.filter(this.getChildren(), function(child){ - return child.region == region; - })[0]._splitterWidget; - }, - - resize: function(newSize, currentSize){ - // Overrides _LayoutWidget.resize(). - - // resetting potential padding to 0px to provide support for 100% width/height + padding - // TODO: this hack doesn't respect the box model and is a temporary fix - if(!this.cs || !this.pe){ - var node = this.domNode; - this.cs = dojo.getComputedStyle(node); - this.pe = dojo._getPadExtents(node, this.cs); - this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight); - this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom); - - dojo.style(node, "padding", "0px"); - } - - this.inherited(arguments); - }, - - _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){ - // summary: - // This is the main routine for setting size/position of each child. - // description: - // With no arguments, measures the height of top/bottom panes, the width - // of left/right panes, and then sizes all panes accordingly. - // - // With changedRegion specified (as "left", "top", "bottom", or "right"), - // it changes that region's width/height to changedRegionSize and - // then resizes other regions that were affected. - // changedChildId: - // Id of the child which should be resized because splitter was dragged. - // changedChildSize: - // The new width/height (in pixels) to make specified child - - if(!this._borderBox || !this._borderBox.h){ - // We are currently hidden, or we haven't been sized by our parent yet. - // Abort. Someone will resize us later. - return; - } - - // Generate list of wrappers of my children in the order that I want layoutChildren() - // to process them (i.e. from the outside to the inside) - var wrappers = dojo.map(this.getChildren(), function(child, idx){ - return { - pane: child, - weight: [ - child.region == "center" ? Infinity : 0, - child.layoutPriority, - (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1), - idx - ] - }; - }, this); - wrappers.sort(function(a, b){ - var aw = a.weight, bw = b.weight; - for(var i=0; i<aw.length; i++){ - if(aw[i] != bw[i]){ - return aw[i] - bw[i]; - } - } - return 0; - }); - - // Make new list, combining the externally specified children with splitters and gutters - var childrenAndSplitters = []; - dojo.forEach(wrappers, function(wrapper){ - var pane = wrapper.pane; - childrenAndSplitters.push(pane); - if(pane._splitterWidget){ - childrenAndSplitters.push(pane._splitterWidget); - } - }); - - // Compute the box in which to lay out my children - var dim = { - l: this.pe.l, - t: this.pe.t, - w: this._borderBox.w - this.pe.w, - h: this._borderBox.h - this.pe.h - }; - - // Layout the children, possibly changing size due to a splitter drag - dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters, - changedChildId, changedChildSize); - }, - - destroyRecursive: function(){ - // Destroy splitters first, while getChildren() still works - dojo.forEach(this.getChildren(), function(child){ - var splitter = child._splitterWidget; - if(splitter){ - splitter.destroy(); - } - delete child._splitterWidget; - }); - - // Then destroy the real children, and myself - this.inherited(arguments); - } -}); - -// This argument can be specified for the children of a BorderContainer. -// Since any widget can be specified as a LayoutContainer child, mix it -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // region: [const] String - // Parameter for children of `dijit.layout.BorderContainer`. - // Values: "top", "bottom", "leading", "trailing", "left", "right", "center". - // See the `dijit.layout.BorderContainer` description for details. - region: '', - - // layoutPriority: [const] Number - // Parameter for children of `dijit.layout.BorderContainer`. - // Children with a higher layoutPriority will be placed closer to the BorderContainer center, - // between children with a lower layoutPriority. - layoutPriority: 0, - - // splitter: [const] Boolean - // Parameter for child of `dijit.layout.BorderContainer` where region != "center". - // If true, enables user to resize the widget by putting a draggable splitter between - // this widget and the region=center widget. - splitter: false, - - // minSize: [const] Number - // Parameter for children of `dijit.layout.BorderContainer`. - // Specifies a minimum size (in pixels) for this widget when resized by a splitter. - minSize: 0, - - // maxSize: [const] Number - // Parameter for children of `dijit.layout.BorderContainer`. - // Specifies a maximum size (in pixels) for this widget when resized by a splitter. - maxSize: Infinity -}); - -dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ], -{ - // summary: - // A draggable spacer between two items in a `dijit.layout.BorderContainer`. - // description: - // This is instantiated by `dijit.layout.BorderContainer`. Users should not - // create it directly. - // tags: - // private - -/*===== - // container: [const] dijit.layout.BorderContainer - // Pointer to the parent BorderContainer - container: null, - - // child: [const] dijit.layout._LayoutWidget - // Pointer to the pane associated with this splitter - child: null, - - // region: [const] String - // Region of pane associated with this splitter. - // "top", "bottom", "left", "right". - region: null, -=====*/ - - // live: [const] Boolean - // If true, the child's size changes and the child widget is redrawn as you drag the splitter; - // otherwise, the size doesn't change until you drop the splitter (by mouse-up) - live: true, - - templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>', - - postMixInProperties: function(){ - this.inherited(arguments); - - this.horizontal = /top|bottom/.test(this.region); - this._factor = /top|left/.test(this.region) ? 1 : -1; - this._cookieName = this.container.id + "_" + this.region; - }, - - buildRendering: function(){ - this.inherited(arguments); - - dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V")); - - if(this.container.persist){ - // restore old size - var persistSize = dojo.cookie(this._cookieName); - if(persistSize){ - this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize; - } - } - }, - - _computeMaxSize: function(){ - // summary: - // Return the maximum size that my corresponding pane can be set to - - var dim = this.horizontal ? 'h' : 'w', - childSize = dojo.marginBox(this.child.domNode)[dim], - center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0], - spaceAvailable = dojo.marginBox(center.domNode)[dim]; // can expand until center is crushed to 0 - - return Math.min(this.child.maxSize, childSize + spaceAvailable); - }, - - _startDrag: function(e){ - if(!this.cover){ - this.cover = dojo.doc.createElement('div'); - dojo.addClass(this.cover, "dijitSplitterCover"); - dojo.place(this.cover, this.child.domNode, "after"); - } - dojo.addClass(this.cover, "dijitSplitterCoverActive"); - - // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up. - if(this.fake){ dojo.destroy(this.fake); } - if(!(this._resize = this.live)){ //TODO: disable live for IE6? - // create fake splitter to display at old position while we drag - (this.fake = this.domNode.cloneNode(true)).removeAttribute("id"); - dojo.addClass(this.domNode, "dijitSplitterShadow"); - dojo.place(this.fake, this.domNode, "after"); - } - dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active"); - if(this.fake){ - dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover"); - } - - //Performance: load data info local vars for onmousevent function closure - var factor = this._factor, - isHorizontal = this.horizontal, - axis = isHorizontal ? "pageY" : "pageX", - pageStart = e[axis], - splitterStyle = this.domNode.style, - dim = isHorizontal ? 'h' : 'w', - childStart = dojo.marginBox(this.child.domNode)[dim], - max = this._computeMaxSize(), - min = this.child.minSize || 20, - region = this.region, - splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust - splitterStart = parseInt(splitterStyle[splitterAttr], 10), - resize = this._resize, - layoutFunc = dojo.hitch(this.container, "_layoutChildren", this.child.id), - de = dojo.doc; - - this._handlers = (this._handlers || []).concat([ - dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){ - var delta = e[axis] - pageStart, - childSize = factor * delta + childStart, - boundChildSize = Math.max(Math.min(childSize, max), min); - - if(resize || forceResize){ - layoutFunc(boundChildSize); - } - // TODO: setting style directly (usually) sets content box size, need to set margin box size - splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px"; - }), - dojo.connect(de, "ondragstart", dojo.stopEvent), - dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent), - dojo.connect(de, "onmouseup", this, "_stopDrag") - ]); - dojo.stopEvent(e); - }, - - _onMouse: function(e){ - var o = (e.type == "mouseover" || e.type == "mouseenter"); - dojo.toggleClass(this.domNode, "dijitSplitterHover", o); - dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o); - }, - - _stopDrag: function(e){ - try{ - if(this.cover){ - dojo.removeClass(this.cover, "dijitSplitterCoverActive"); - } - if(this.fake){ dojo.destroy(this.fake); } - dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter" - + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow"); - this._drag(e); //TODO: redundant with onmousemove? - this._drag(e, true); - }finally{ - this._cleanupHandlers(); - delete this._drag; - } - - if(this.container.persist){ - dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365}); - } - }, - - _cleanupHandlers: function(){ - dojo.forEach(this._handlers, dojo.disconnect); - delete this._handlers; - }, - - _onKeyPress: function(/*Event*/ e){ - // should we apply typematic to this? - this._resize = true; - var horizontal = this.horizontal; - var tick = 1; - var dk = dojo.keys; - switch(e.charOrCode){ - case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW: - tick *= -1; -// break; - case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW: - break; - default: -// this.inherited(arguments); - return; - } - var childSize = dojo._getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick; - this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize)); - dojo.stopEvent(e); - }, - - destroy: function(){ - this._cleanupHandlers(); - delete this.child; - delete this.container; - delete this.cover; - delete this.fake; - this.inherited(arguments); - } -}); - -dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated], -{ - // summary: - // Just a spacer div to separate side pane from center pane. - // Basically a trick to lookup the gutter/splitter width from the theme. - // description: - // Instantiated by `dijit.layout.BorderContainer`. Users should not - // create directly. - // tags: - // private - - templateString: '<div class="dijitGutter" role="presentation"></div>', - - postMixInProperties: function(){ - this.inherited(arguments); - this.horizontal = /top|bottom/.test(this.region); - }, - - buildRendering: function(){ - this.inherited(arguments); - dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V")); - } -}); - -} +//>>built +define("dijit/layout/BorderContainer",["dojo/_base/array","dojo/cookie","dojo/_base/declare","dojo/dom-class","dojo/dom-construct","dojo/dom-geometry","dojo/dom-style","dojo/_base/event","dojo/keys","dojo/_base/lang","dojo/on","dojo/touch","dojo/_base/window","../_WidgetBase","../_Widget","../_TemplatedMixin","./_LayoutWidget","./utils"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,on,_b,_c,_d,_e,_f,_10,_11){var _12=_3("dijit.layout._Splitter",[_e,_f],{live:true,templateString:"<div class=\"dijitSplitter\" data-dojo-attach-event=\"onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse\" tabIndex=\"0\" role=\"separator\"><div class=\"dijitSplitterThumb\"></div></div>",constructor:function(){this._handlers=[];},postMixInProperties:function(){this.inherited(arguments);this.horizontal=/top|bottom/.test(this.region);this._factor=/top|left/.test(this.region)?1:-1;this._cookieName=this.container.id+"_"+this.region;},buildRendering:function(){this.inherited(arguments);_4.add(this.domNode,"dijitSplitter"+(this.horizontal?"H":"V"));if(this.container.persist){var _13=_2(this._cookieName);if(_13){this.child.domNode.style[this.horizontal?"height":"width"]=_13;}}},_computeMaxSize:function(){var dim=this.horizontal?"h":"w",_14=_6.getMarginBox(this.child.domNode)[dim],_15=_1.filter(this.container.getChildren(),function(_16){return _16.region=="center";})[0],_17=_6.getMarginBox(_15.domNode)[dim];return Math.min(this.child.maxSize,_14+_17);},_startDrag:function(e){if(!this.cover){this.cover=_c.doc.createElement("div");_4.add(this.cover,"dijitSplitterCover");_5.place(this.cover,this.child.domNode,"after");}_4.add(this.cover,"dijitSplitterCoverActive");if(this.fake){_5.destroy(this.fake);}if(!(this._resize=this.live)){(this.fake=this.domNode.cloneNode(true)).removeAttribute("id");_4.add(this.domNode,"dijitSplitterShadow");_5.place(this.fake,this.domNode,"after");}_4.add(this.domNode,"dijitSplitterActive dijitSplitter"+(this.horizontal?"H":"V")+"Active");if(this.fake){_4.remove(this.fake,"dijitSplitterHover dijitSplitter"+(this.horizontal?"H":"V")+"Hover");}var _18=this._factor,_19=this.horizontal,_1a=_19?"pageY":"pageX",_1b=e[_1a],_1c=this.domNode.style,dim=_19?"h":"w",_1d=_6.getMarginBox(this.child.domNode)[dim],max=this._computeMaxSize(),min=this.child.minSize||20,_1e=this.region,_1f=_1e=="top"||_1e=="bottom"?"top":"left",_20=parseInt(_1c[_1f],10),_21=this._resize,_22=_a.hitch(this.container,"_layoutChildren",this.child.id),de=_c.doc;this._handlers=this._handlers.concat([on(de,_b.move,this._drag=function(e,_23){var _24=e[_1a]-_1b,_25=_18*_24+_1d,_26=Math.max(Math.min(_25,max),min);if(_21||_23){_22(_26);}_1c[_1f]=_24+_20+_18*(_26-_25)+"px";}),on(de,"dragstart",_8.stop),on(_c.body(),"selectstart",_8.stop),on(de,_b.release,_a.hitch(this,"_stopDrag"))]);_8.stop(e);},_onMouse:function(e){var o=(e.type=="mouseover"||e.type=="mouseenter");_4.toggle(this.domNode,"dijitSplitterHover",o);_4.toggle(this.domNode,"dijitSplitter"+(this.horizontal?"H":"V")+"Hover",o);},_stopDrag:function(e){try{if(this.cover){_4.remove(this.cover,"dijitSplitterCoverActive");}if(this.fake){_5.destroy(this.fake);}_4.remove(this.domNode,"dijitSplitterActive dijitSplitter"+(this.horizontal?"H":"V")+"Active dijitSplitterShadow");this._drag(e);this._drag(e,true);}finally{this._cleanupHandlers();delete this._drag;}if(this.container.persist){_2(this._cookieName,this.child.domNode.style[this.horizontal?"height":"width"],{expires:365});}},_cleanupHandlers:function(){var h;while(h=this._handlers.pop()){h.remove();}},_onKeyPress:function(e){this._resize=true;var _27=this.horizontal;var _28=1;switch(e.charOrCode){case _27?_9.UP_ARROW:_9.LEFT_ARROW:_28*=-1;case _27?_9.DOWN_ARROW:_9.RIGHT_ARROW:break;default:return;}var _29=_6.getMarginSize(this.child.domNode)[_27?"h":"w"]+this._factor*_28;this.container._layoutChildren(this.child.id,Math.max(Math.min(_29,this._computeMaxSize()),this.child.minSize));_8.stop(e);},destroy:function(){this._cleanupHandlers();delete this.child;delete this.container;delete this.cover;delete this.fake;this.inherited(arguments);}});var _2a=_3("dijit.layout._Gutter",[_e,_f],{templateString:"<div class=\"dijitGutter\" role=\"presentation\"></div>",postMixInProperties:function(){this.inherited(arguments);this.horizontal=/top|bottom/.test(this.region);},buildRendering:function(){this.inherited(arguments);_4.add(this.domNode,"dijitGutter"+(this.horizontal?"H":"V"));}});var _2b=_3("dijit.layout.BorderContainer",_10,{design:"headline",gutters:true,liveSplitters:true,persist:false,baseClass:"dijitBorderContainer",_splitterClass:_12,postMixInProperties:function(){if(!this.gutters){this.baseClass+="NoGutter";}this.inherited(arguments);},startup:function(){if(this._started){return;}_1.forEach(this.getChildren(),this._setupChild,this);this.inherited(arguments);},_setupChild:function(_2c){var _2d=_2c.region;if(_2d){this.inherited(arguments);_4.add(_2c.domNode,this.baseClass+"Pane");var ltr=this.isLeftToRight();if(_2d=="leading"){_2d=ltr?"left":"right";}if(_2d=="trailing"){_2d=ltr?"right":"left";}if(_2d!="center"&&(_2c.splitter||this.gutters)&&!_2c._splitterWidget){var _2e=_2c.splitter?this._splitterClass:_2a;if(_a.isString(_2e)){_2e=_a.getObject(_2e);}var _2f=new _2e({id:_2c.id+"_splitter",container:this,child:_2c,region:_2d,live:this.liveSplitters});_2f.isSplitter=true;_2c._splitterWidget=_2f;_5.place(_2f.domNode,_2c.domNode,"after");_2f.startup();}_2c.region=_2d;}},layout:function(){this._layoutChildren();},addChild:function(_30,_31){this.inherited(arguments);if(this._started){this.layout();}},removeChild:function(_32){var _33=_32.region;var _34=_32._splitterWidget;if(_34){_34.destroy();delete _32._splitterWidget;}this.inherited(arguments);if(this._started){this._layoutChildren();}_4.remove(_32.domNode,this.baseClass+"Pane");_7.set(_32.domNode,{top:"auto",bottom:"auto",left:"auto",right:"auto",position:"static"});_7.set(_32.domNode,_33=="top"||_33=="bottom"?"width":"height","auto");},getChildren:function(){return _1.filter(this.inherited(arguments),function(_35){return !_35.isSplitter;});},getSplitter:function(_36){return _1.filter(this.getChildren(),function(_37){return _37.region==_36;})[0]._splitterWidget;},resize:function(_38,_39){if(!this.cs||!this.pe){var _3a=this.domNode;this.cs=_7.getComputedStyle(_3a);this.pe=_6.getPadExtents(_3a,this.cs);this.pe.r=_7.toPixelValue(_3a,this.cs.paddingRight);this.pe.b=_7.toPixelValue(_3a,this.cs.paddingBottom);_7.set(_3a,"padding","0px");}this.inherited(arguments);},_layoutChildren:function(_3b,_3c){if(!this._borderBox||!this._borderBox.h){return;}var _3d=_1.map(this.getChildren(),function(_3e,idx){return {pane:_3e,weight:[_3e.region=="center"?Infinity:0,_3e.layoutPriority,(this.design=="sidebar"?1:-1)*(/top|bottom/.test(_3e.region)?1:-1),idx]};},this);_3d.sort(function(a,b){var aw=a.weight,bw=b.weight;for(var i=0;i<aw.length;i++){if(aw[i]!=bw[i]){return aw[i]-bw[i];}}return 0;});var _3f=[];_1.forEach(_3d,function(_40){var _41=_40.pane;_3f.push(_41);if(_41._splitterWidget){_3f.push(_41._splitterWidget);}});var dim={l:this.pe.l,t:this.pe.t,w:this._borderBox.w-this.pe.w,h:this._borderBox.h-this.pe.h};_11.layoutChildren(this.domNode,dim,_3f,_3b,_3c);},destroyRecursive:function(){_1.forEach(this.getChildren(),function(_42){var _43=_42._splitterWidget;if(_43){_43.destroy();}delete _42._splitterWidget;});this.inherited(arguments);}});_a.extend(_d,{region:"",layoutPriority:0,splitter:false,minSize:0,maxSize:Infinity});_2b._Splitter=_12;_2b._Gutter=_2a;return _2b;});
\ No newline at end of file diff --git a/lib/dijit/layout/BorderContainer.js.uncompressed.js b/lib/dijit/layout/BorderContainer.js.uncompressed.js new file mode 100644 index 000000000..3ae97c0e7 --- /dev/null +++ b/lib/dijit/layout/BorderContainer.js.uncompressed.js @@ -0,0 +1,556 @@ +define("dijit/layout/BorderContainer", [ + "dojo/_base/array", // array.filter array.forEach array.map + "dojo/cookie", // cookie + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add domClass.remove domClass.toggle + "dojo/dom-construct", // domConstruct.destroy domConstruct.place + "dojo/dom-geometry", // domGeometry.marginBox + "dojo/dom-style", // domStyle.style + "dojo/_base/event", // event.stop + "dojo/keys", + "dojo/_base/lang", // lang.getObject lang.hitch + "dojo/on", + "dojo/touch", + "dojo/_base/window", // win.body win.doc win.doc.createElement + "../_WidgetBase", + "../_Widget", + "../_TemplatedMixin", + "./_LayoutWidget", + "./utils" // layoutUtils.layoutChildren +], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch, win, + _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){ + +/*===== + var _WidgetBase = dijit._WidgetBase; + var _Widget = dijit._Widget; + var _TemplatedMixin = dijit._TemplatedMixin; + var _LayoutWidget = dijit.layout._LayoutWidget; +=====*/ + +// module: +// dijit/layout/BorderContainer +// summary: +// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides. + +var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ], +{ + // summary: + // A draggable spacer between two items in a `dijit.layout.BorderContainer`. + // description: + // This is instantiated by `dijit.layout.BorderContainer`. Users should not + // create it directly. + // tags: + // private + +/*===== + // container: [const] dijit.layout.BorderContainer + // Pointer to the parent BorderContainer + container: null, + + // child: [const] dijit.layout._LayoutWidget + // Pointer to the pane associated with this splitter + child: null, + + // region: [const] String + // Region of pane associated with this splitter. + // "top", "bottom", "left", "right". + region: null, +=====*/ + + // live: [const] Boolean + // If true, the child's size changes and the child widget is redrawn as you drag the splitter; + // otherwise, the size doesn't change until you drop the splitter (by mouse-up) + live: true, + + templateString: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>', + + constructor: function(){ + this._handlers = []; + }, + + postMixInProperties: function(){ + this.inherited(arguments); + + this.horizontal = /top|bottom/.test(this.region); + this._factor = /top|left/.test(this.region) ? 1 : -1; + this._cookieName = this.container.id + "_" + this.region; + }, + + buildRendering: function(){ + this.inherited(arguments); + + domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V")); + + if(this.container.persist){ + // restore old size + var persistSize = cookie(this._cookieName); + if(persistSize){ + this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize; + } + } + }, + + _computeMaxSize: function(){ + // summary: + // Return the maximum size that my corresponding pane can be set to + + var dim = this.horizontal ? 'h' : 'w', + childSize = domGeometry.getMarginBox(this.child.domNode)[dim], + center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0], + spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0 + + return Math.min(this.child.maxSize, childSize + spaceAvailable); + }, + + _startDrag: function(e){ + if(!this.cover){ + this.cover = win.doc.createElement('div'); + domClass.add(this.cover, "dijitSplitterCover"); + domConstruct.place(this.cover, this.child.domNode, "after"); + } + domClass.add(this.cover, "dijitSplitterCoverActive"); + + // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up. + if(this.fake){ domConstruct.destroy(this.fake); } + if(!(this._resize = this.live)){ //TODO: disable live for IE6? + // create fake splitter to display at old position while we drag + (this.fake = this.domNode.cloneNode(true)).removeAttribute("id"); + domClass.add(this.domNode, "dijitSplitterShadow"); + domConstruct.place(this.fake, this.domNode, "after"); + } + domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active"); + if(this.fake){ + domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover"); + } + + //Performance: load data info local vars for onmousevent function closure + var factor = this._factor, + isHorizontal = this.horizontal, + axis = isHorizontal ? "pageY" : "pageX", + pageStart = e[axis], + splitterStyle = this.domNode.style, + dim = isHorizontal ? 'h' : 'w', + childStart = domGeometry.getMarginBox(this.child.domNode)[dim], + max = this._computeMaxSize(), + min = this.child.minSize || 20, + region = this.region, + splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust + splitterStart = parseInt(splitterStyle[splitterAttr], 10), + resize = this._resize, + layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id), + de = win.doc; + + this._handlers = this._handlers.concat([ + on(de, touch.move, this._drag = function(e, forceResize){ + var delta = e[axis] - pageStart, + childSize = factor * delta + childStart, + boundChildSize = Math.max(Math.min(childSize, max), min); + + if(resize || forceResize){ + layoutFunc(boundChildSize); + } + // TODO: setting style directly (usually) sets content box size, need to set margin box size + splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px"; + }), + on(de, "dragstart", event.stop), + on(win.body(), "selectstart", event.stop), + on(de, touch.release, lang.hitch(this, "_stopDrag")) + ]); + event.stop(e); + }, + + _onMouse: function(e){ + // summary: + // Handler for onmouseenter / onmouseleave events + var o = (e.type == "mouseover" || e.type == "mouseenter"); + domClass.toggle(this.domNode, "dijitSplitterHover", o); + domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o); + }, + + _stopDrag: function(e){ + try{ + if(this.cover){ + domClass.remove(this.cover, "dijitSplitterCoverActive"); + } + if(this.fake){ domConstruct.destroy(this.fake); } + domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter" + + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow"); + this._drag(e); //TODO: redundant with onmousemove? + this._drag(e, true); + }finally{ + this._cleanupHandlers(); + delete this._drag; + } + + if(this.container.persist){ + cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365}); + } + }, + + _cleanupHandlers: function(){ + var h; + while(h = this._handlers.pop()){ h.remove(); } + }, + + _onKeyPress: function(/*Event*/ e){ + // should we apply typematic to this? + this._resize = true; + var horizontal = this.horizontal; + var tick = 1; + switch(e.charOrCode){ + case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW: + tick *= -1; +// break; + case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW: + break; + default: +// this.inherited(arguments); + return; + } + var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick; + this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize)); + event.stop(e); + }, + + destroy: function(){ + this._cleanupHandlers(); + delete this.child; + delete this.container; + delete this.cover; + delete this.fake; + this.inherited(arguments); + } +}); + +var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin], +{ + // summary: + // Just a spacer div to separate side pane from center pane. + // Basically a trick to lookup the gutter/splitter width from the theme. + // description: + // Instantiated by `dijit.layout.BorderContainer`. Users should not + // create directly. + // tags: + // private + + templateString: '<div class="dijitGutter" role="presentation"></div>', + + postMixInProperties: function(){ + this.inherited(arguments); + this.horizontal = /top|bottom/.test(this.region); + }, + + buildRendering: function(){ + this.inherited(arguments); + domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V")); + } +}); + +var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, { + // summary: + // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides. + // + // description: + // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;", + // that contains a child widget marked region="center" and optionally children widgets marked + // region equal to "top", "bottom", "leading", "trailing", "left" or "right". + // Children along the edges will be laid out according to width or height dimensions and may + // include optional splitters (splitter="true") to make them resizable by the user. The remaining + // space is designated for the center region. + // + // The outer size must be specified on the BorderContainer node. Width must be specified for the sides + // and height for the top and bottom, respectively. No dimensions should be specified on the center; + // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like + // "left" and "right" except that they will be reversed in right-to-left environments. + // + // For complex layouts, multiple children can be specified for a single region. In this case, the + // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority) + // and which child is closer to the center (high layoutPriority). layoutPriority can also be used + // instead of the design attribute to control layout precedence of horizontal vs. vertical panes. + // example: + // | <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'sidebar', gutters: false" + // | style="width: 400px; height: 300px;"> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'top'">header text</div> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'center'">client area</div> + // | </div> + + // design: String + // Which design is used for the layout: + // - "headline" (default) where the top and bottom extend + // the full width of the container + // - "sidebar" where the left and right sides extend from top to bottom. + design: "headline", + + // gutters: [const] Boolean + // Give each pane a border and margin. + // Margin determined by domNode.paddingLeft. + // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing. + gutters: true, + + // liveSplitters: [const] Boolean + // Specifies whether splitters resize as you drag (true) or only upon mouseup (false) + liveSplitters: true, + + // persist: Boolean + // Save splitter positions in a cookie. + persist: false, + + baseClass: "dijitBorderContainer", + + // _splitterClass: Function||String + // Optional hook to override the default Splitter widget used by BorderContainer + _splitterClass: _Splitter, + + postMixInProperties: function(){ + // change class name to indicate that BorderContainer is being used purely for + // layout (like LayoutContainer) rather than for pretty formatting. + if(!this.gutters){ + this.baseClass += "NoGutter"; + } + this.inherited(arguments); + }, + + startup: function(){ + if(this._started){ return; } + array.forEach(this.getChildren(), this._setupChild, this); + this.inherited(arguments); + }, + + _setupChild: function(/*dijit._Widget*/ child){ + // Override _LayoutWidget._setupChild(). + + var region = child.region; + if(region){ + this.inherited(arguments); + + domClass.add(child.domNode, this.baseClass+"Pane"); + + var ltr = this.isLeftToRight(); + if(region == "leading"){ region = ltr ? "left" : "right"; } + if(region == "trailing"){ region = ltr ? "right" : "left"; } + + // Create draggable splitter for resizing pane, + // or alternately if splitter=false but BorderContainer.gutters=true then + // insert dummy div just for spacing + if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){ + var _Splitter = child.splitter ? this._splitterClass : _Gutter; + if(lang.isString(_Splitter)){ + _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0 + } + var splitter = new _Splitter({ + id: child.id + "_splitter", + container: this, + child: child, + region: region, + live: this.liveSplitters + }); + splitter.isSplitter = true; + child._splitterWidget = splitter; + + domConstruct.place(splitter.domNode, child.domNode, "after"); + + // Splitters aren't added as Contained children, so we need to call startup explicitly + splitter.startup(); + } + child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc. + } + }, + + layout: function(){ + // Implement _LayoutWidget.layout() virtual method. + this._layoutChildren(); + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + // Override _LayoutWidget.addChild(). + this.inherited(arguments); + if(this._started){ + this.layout(); //OPT + } + }, + + removeChild: function(/*dijit._Widget*/ child){ + // Override _LayoutWidget.removeChild(). + + var region = child.region; + var splitter = child._splitterWidget; + if(splitter){ + splitter.destroy(); + delete child._splitterWidget; + } + this.inherited(arguments); + + if(this._started){ + this._layoutChildren(); + } + // Clean up whatever style changes we made to the child pane. + // Unclear how height and width should be handled. + domClass.remove(child.domNode, this.baseClass+"Pane"); + domStyle.set(child.domNode, { + top: "auto", + bottom: "auto", + left: "auto", + right: "auto", + position: "static" + }); + domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto"); + }, + + getChildren: function(){ + // Override _LayoutWidget.getChildren() to only return real children, not the splitters. + return array.filter(this.inherited(arguments), function(widget){ + return !widget.isSplitter; + }); + }, + + // TODO: remove in 2.0 + getSplitter: function(/*String*/region){ + // summary: + // Returns the widget responsible for rendering the splitter associated with region + // tags: + // deprecated + return array.filter(this.getChildren(), function(child){ + return child.region == region; + })[0]._splitterWidget; + }, + + resize: function(newSize, currentSize){ + // Overrides _LayoutWidget.resize(). + + // resetting potential padding to 0px to provide support for 100% width/height + padding + // TODO: this hack doesn't respect the box model and is a temporary fix + if(!this.cs || !this.pe){ + var node = this.domNode; + this.cs = domStyle.getComputedStyle(node); + this.pe = domGeometry.getPadExtents(node, this.cs); + this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight); + this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom); + + domStyle.set(node, "padding", "0px"); + } + + this.inherited(arguments); + }, + + _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){ + // summary: + // This is the main routine for setting size/position of each child. + // description: + // With no arguments, measures the height of top/bottom panes, the width + // of left/right panes, and then sizes all panes accordingly. + // + // With changedRegion specified (as "left", "top", "bottom", or "right"), + // it changes that region's width/height to changedRegionSize and + // then resizes other regions that were affected. + // changedChildId: + // Id of the child which should be resized because splitter was dragged. + // changedChildSize: + // The new width/height (in pixels) to make specified child + + if(!this._borderBox || !this._borderBox.h){ + // We are currently hidden, or we haven't been sized by our parent yet. + // Abort. Someone will resize us later. + return; + } + + // Generate list of wrappers of my children in the order that I want layoutChildren() + // to process them (i.e. from the outside to the inside) + var wrappers = array.map(this.getChildren(), function(child, idx){ + return { + pane: child, + weight: [ + child.region == "center" ? Infinity : 0, + child.layoutPriority, + (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1), + idx + ] + }; + }, this); + wrappers.sort(function(a, b){ + var aw = a.weight, bw = b.weight; + for(var i=0; i<aw.length; i++){ + if(aw[i] != bw[i]){ + return aw[i] - bw[i]; + } + } + return 0; + }); + + // Make new list, combining the externally specified children with splitters and gutters + var childrenAndSplitters = []; + array.forEach(wrappers, function(wrapper){ + var pane = wrapper.pane; + childrenAndSplitters.push(pane); + if(pane._splitterWidget){ + childrenAndSplitters.push(pane._splitterWidget); + } + }); + + // Compute the box in which to lay out my children + var dim = { + l: this.pe.l, + t: this.pe.t, + w: this._borderBox.w - this.pe.w, + h: this._borderBox.h - this.pe.h + }; + + // Layout the children, possibly changing size due to a splitter drag + layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters, + changedChildId, changedChildSize); + }, + + destroyRecursive: function(){ + // Destroy splitters first, while getChildren() still works + array.forEach(this.getChildren(), function(child){ + var splitter = child._splitterWidget; + if(splitter){ + splitter.destroy(); + } + delete child._splitterWidget; + }); + + // Then destroy the real children, and myself + this.inherited(arguments); + } +}); + +// This argument can be specified for the children of a BorderContainer. +// Since any widget can be specified as a LayoutContainer child, mix it +// into the base widget class. (This is a hack, but it's effective.) +lang.extend(_WidgetBase, { + // region: [const] String + // Parameter for children of `dijit.layout.BorderContainer`. + // Values: "top", "bottom", "leading", "trailing", "left", "right", "center". + // See the `dijit.layout.BorderContainer` description for details. + region: '', + + // layoutPriority: [const] Number + // Parameter for children of `dijit.layout.BorderContainer`. + // Children with a higher layoutPriority will be placed closer to the BorderContainer center, + // between children with a lower layoutPriority. + layoutPriority: 0, + + // splitter: [const] Boolean + // Parameter for child of `dijit.layout.BorderContainer` where region != "center". + // If true, enables user to resize the widget by putting a draggable splitter between + // this widget and the region=center widget. + splitter: false, + + // minSize: [const] Number + // Parameter for children of `dijit.layout.BorderContainer`. + // Specifies a minimum size (in pixels) for this widget when resized by a splitter. + minSize: 0, + + // maxSize: [const] Number + // Parameter for children of `dijit.layout.BorderContainer`. + // Specifies a maximum size (in pixels) for this widget when resized by a splitter. + maxSize: Infinity +}); + +// For monkey patching +BorderContainer._Splitter = _Splitter; +BorderContainer._Gutter = _Gutter; + +return BorderContainer; +}); diff --git a/lib/dijit/layout/ContentPane.js b/lib/dijit/layout/ContentPane.js index 399ec1bc9..1e2a2036b 100644 --- a/lib/dijit/layout/ContentPane.js +++ b/lib/dijit/layout/ContentPane.js @@ -1,593 +1,2 @@ -/* - 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.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.ContentPane"] = true; -dojo.provide("dijit.layout.ContentPane"); -dojo.require("dijit._Widget"); -dojo.require("dijit.layout._ContentPaneResizeMixin"); -dojo.require("dojo.string"); -dojo.require("dojo.html"); -dojo.requireLocalization("dijit", "loading", 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"); - - -dojo.declare( - "dijit.layout.ContentPane", [dijit._Widget, dijit.layout._ContentPaneResizeMixin], -{ - // summary: - // A widget containing an HTML fragment, specified inline - // or by uri. Fragment may include widgets. - // - // description: - // This widget embeds a document fragment in the page, specified - // either by uri, javascript generated markup or DOM reference. - // Any widgets within this content are instantiated and managed, - // but laid out according to the HTML structure. Unlike IFRAME, - // ContentPane embeds a document fragment as would be found - // inside the BODY tag of a full HTML document. It should not - // contain the HTML, HEAD, or BODY tags. - // For more advanced functionality with scripts and - // stylesheets, see dojox.layout.ContentPane. This widget may be - // used stand alone or as a base class for other widgets. - // ContentPane is useful as a child of other layout containers - // such as BorderContainer or TabContainer, but note that those - // widgets can contain any widget as a child. - // - // example: - // Some quick samples: - // To change the innerHTML: cp.set('content', '<b>new content</b>') - // - // Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection)) - // - // To do an ajax update: cp.set('href', url) - - // href: String - // The href of the content that displays now. - // Set this at construction if you want to load data externally when the - // pane is shown. (Set preload=true to load it immediately.) - // Changing href after creation doesn't have any effect; Use set('href', ...); - href: "", - -/*===== - // content: String || DomNode || NodeList || dijit._Widget - // The innerHTML of the ContentPane. - // Note that the initialization parameter / argument to set("content", ...) - // can be a String, DomNode, Nodelist, or _Widget. - content: "", -=====*/ - - // extractContent: Boolean - // Extract visible content from inside of <body> .... </body>. - // I.e., strip <html> and <head> (and it's contents) from the href - extractContent: false, - - // parseOnLoad: Boolean - // Parse content and create the widgets, if any. - parseOnLoad: true, - - // parserScope: String - // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo, - // will search for data-dojo-type (or dojoType). For backwards compatibility - // reasons defaults to dojo._scopeName (which is "dojo" except when - // multi-version support is used, when it will be something like dojo16, dojo20, etc.) - parserScope: dojo._scopeName, - - // preventCache: Boolean - // Prevent caching of data from href's by appending a timestamp to the href. - preventCache: false, - - // preload: Boolean - // Force load of data on initialization even if pane is hidden. - preload: false, - - // refreshOnShow: Boolean - // Refresh (re-download) content when pane goes from hidden to shown - refreshOnShow: false, - - // loadingMessage: String - // Message that shows while downloading - loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>", - - // errorMessage: String - // Message that shows if an error occurs - errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>", - - // isLoaded: [readonly] Boolean - // True if the ContentPane has data in it, either specified - // during initialization (via href or inline content), or set - // via set('content', ...) / set('href', ...) - // - // False if it doesn't have any content, or if ContentPane is - // still in the process of downloading href. - isLoaded: false, - - baseClass: "dijitContentPane", - - // ioArgs: Object - // Parameters to pass to xhrGet() request, for example: - // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}"> - ioArgs: {}, - - // onLoadDeferred: [readonly] dojo.Deferred - // This is the `dojo.Deferred` returned by set('href', ...) and refresh(). - // Calling onLoadDeferred.addCallback() or addErrback() registers your - // callback to be called only once, when the prior set('href', ...) call or - // the initial href parameter to the constructor finishes loading. - // - // This is different than an onLoad() handler which gets called any time any href - // or content is loaded. - onLoadDeferred: null, - - // Override _Widget's attributeMap because we don't want the title attribute (used to specify - // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the - // entire pane. - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - title: [] - }), - - // Flag to parser that I'll parse my contents, so it shouldn't. - stopParser: true, - - // template: [private] Boolean - // Flag from the parser that this ContentPane is inside a template - // so the contents are pre-parsed. - // (TODO: this declaration can be commented out in 2.0) - template: false, - - create: function(params, srcNodeRef){ - // Convert a srcNodeRef argument into a content parameter, so that the original contents are - // processed in the same way as contents set via set("content", ...), calling the parser etc. - // Avoid modifying original params object since that breaks NodeList instantiation, see #11906. - if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){ - var df = dojo.doc.createDocumentFragment(); - srcNodeRef = dojo.byId(srcNodeRef) - while(srcNodeRef.firstChild){ - df.appendChild(srcNodeRef.firstChild); - } - params = dojo.delegate(params, {content: df}); - } - this.inherited(arguments, [params, srcNodeRef]); - }, - - postMixInProperties: function(){ - this.inherited(arguments); - var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang); - this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages); - this.errorMessage = dojo.string.substitute(this.errorMessage, messages); - }, - - buildRendering: function(){ - this.inherited(arguments); - - // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work. - // For subclasses of ContentPane that do have a template, does nothing. - if(!this.containerNode){ - this.containerNode = this.domNode; - } - - // remove the title attribute so it doesn't show up when hovering - // over a node (TODO: remove in 2.0, no longer needed after #11490) - this.domNode.title = ""; - - if(!dojo.attr(this.domNode,"role")){ - dijit.setWaiRole(this.domNode, "group"); - } - }, - - _startChildren: function(){ - // summary: - // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects - - // This starts all the widgets - this.inherited(arguments); - - // And this catches stuff like dojo.dnd.Source - if(this._contentSetter){ - dojo.forEach(this._contentSetter.parseResults, function(obj){ - if(!obj._started && !obj._destroyed && dojo.isFunction(obj.startup)){ - obj.startup(); - obj._started = true; - } - }, this); - } - }, - - setHref: function(/*String|Uri*/ href){ - // summary: - // Deprecated. Use set('href', ...) instead. - dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0"); - return this.set("href", href); - }, - _setHrefAttr: function(/*String|Uri*/ href){ - // summary: - // Hook so set("href", ...) works. - // description: - // Reset the (external defined) content of this pane and replace with new url - // Note: It delays the download until widget is shown if preload is false. - // href: - // url to the page you want to get, must be within the same domain as your mainpage - - // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...)) - this.cancel(); - - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad")); - - this._set("href", href); - - // _setHrefAttr() is called during creation and by the user, after creation. - // Assuming preload == false, only in the second case do we actually load the URL; - // otherwise it's done in startup(), and only if this widget is shown. - if(this.preload || (this._created && this._isShown())){ - this._load(); - }else{ - // Set flag to indicate that href needs to be loaded the next time the - // ContentPane is made visible - this._hrefChanged = true; - } - - return this.onLoadDeferred; // dojo.Deferred - }, - - setContent: function(/*String|DomNode|Nodelist*/data){ - // summary: - // Deprecated. Use set('content', ...) instead. - dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0"); - this.set("content", data); - }, - _setContentAttr: function(/*String|DomNode|Nodelist*/data){ - // summary: - // Hook to make set("content", ...) work. - // Replaces old content with data content, include style classes from old content - // data: - // the new Content may be String, DomNode or NodeList - // - // if data is a NodeList (or an array of nodes) nodes are copied - // so you can import nodes from another document implicitly - - // clear href so we can't run refresh and clear content - // refresh should only work if we downloaded the content - this._set("href", ""); - - // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...)) - this.cancel(); - - // Even though user is just setting content directly, still need to define an onLoadDeferred - // because the _onLoadHandler() handler is still getting called from setContent() - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - if(this._created){ - // For back-compat reasons, call onLoad() for set('content', ...) - // calls but not for content specified in srcNodeRef (ie: <div dojoType=ContentPane>...</div>) - // or as initialization parameter (ie: new ContentPane({content: ...}) - this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad")); - } - - this._setContent(data || ""); - - this._isDownloaded = false; // mark that content is from a set('content') not a set('href') - - return this.onLoadDeferred; // dojo.Deferred - }, - _getContentAttr: function(){ - // summary: - // Hook to make get("content") work - return this.containerNode.innerHTML; - }, - - cancel: function(){ - // summary: - // Cancels an in-flight download of content - if(this._xhrDfd && (this._xhrDfd.fired == -1)){ - this._xhrDfd.cancel(); - } - delete this._xhrDfd; // garbage collect - - this.onLoadDeferred = null; - }, - - uninitialize: function(){ - if(this._beingDestroyed){ - this.cancel(); - } - this.inherited(arguments); - }, - - destroyRecursive: function(/*Boolean*/ preserveDom){ - // summary: - // Destroy the ContentPane and its contents - - // if we have multiple controllers destroying us, bail after the first - if(this._beingDestroyed){ - return; - } - this.inherited(arguments); - }, - - _onShow: function(){ - // summary: - // Called when the ContentPane is made visible - // description: - // For a plain ContentPane, this is called on initialization, from startup(). - // If the ContentPane is a hidden pane of a TabContainer etc., then it's - // called whenever the pane is made visible. - // - // Does necessary processing, including href download and layout/resize of - // child widget(s) - - this.inherited(arguments); - - if(this.href){ - if(!this._xhrDfd && // if there's an href that isn't already being loaded - (!this.isLoaded || this._hrefChanged || this.refreshOnShow) - ){ - return this.refresh(); // If child has an href, promise that fires when the load is complete - } - } - }, - - refresh: function(){ - // summary: - // [Re]download contents of href and display - // description: - // 1. cancels any currently in-flight requests - // 2. posts "loading..." message - // 3. sends XHR to download new data - - // Cancel possible prior in-flight request - this.cancel(); - - this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel")); - this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad")); - this._load(); - return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete - }, - - _load: function(){ - // summary: - // Load/reload the href specified in this.href - - // display loading message - this._setContent(this.onDownloadStart(), true); - - var self = this; - var getArgs = { - preventCache: (this.preventCache || this.refreshOnShow), - url: this.href, - handleAs: "text" - }; - if(dojo.isObject(this.ioArgs)){ - dojo.mixin(getArgs, this.ioArgs); - } - - var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs)); - - hand.addCallback(function(html){ - try{ - self._isDownloaded = true; - self._setContent(html, false); - self.onDownloadEnd(); - }catch(err){ - self._onError('Content', err); // onContentError - } - delete self._xhrDfd; - return html; - }); - - hand.addErrback(function(err){ - if(!hand.canceled){ - // show error message in the pane - self._onError('Download', err); // onDownloadError - } - delete self._xhrDfd; - return err; - }); - - // Remove flag saying that a load is needed - delete this._hrefChanged; - }, - - _onLoadHandler: function(data){ - // summary: - // This is called whenever new content is being loaded - this._set("isLoaded", true); - try{ - this.onLoadDeferred.callback(data); - }catch(e){ - console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message); - } - }, - - _onUnloadHandler: function(){ - // summary: - // This is called whenever the content is being unloaded - this._set("isLoaded", false); - try{ - this.onUnload(); - }catch(e){ - console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message); - } - }, - - destroyDescendants: function(){ - // summary: - // Destroy all the widgets inside the ContentPane and empty containerNode - - // Make sure we call onUnload (but only when the ContentPane has real content) - if(this.isLoaded){ - this._onUnloadHandler(); - } - - // Even if this.isLoaded == false there might still be a "Loading..." message - // to erase, so continue... - - // For historical reasons we need to delete all widgets under this.containerNode, - // even ones that the user has created manually. - var setter = this._contentSetter; - dojo.forEach(this.getChildren(), function(widget){ - if(widget.destroyRecursive){ - widget.destroyRecursive(); - } - }); - if(setter){ - // Most of the widgets in setter.parseResults have already been destroyed, but - // things like Menu that have been moved to <body> haven't yet - dojo.forEach(setter.parseResults, function(widget){ - if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){ - widget.destroyRecursive(); - } - }); - delete setter.parseResults; - } - - // And then clear away all the DOM nodes - dojo.html._emptyNode(this.containerNode); - - // Delete any state information we have about current contents - delete this._singleChild; - }, - - _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){ - // summary: - // Insert the content into the container node - - // first get rid of child widgets - this.destroyDescendants(); - - // dojo.html.set will take care of the rest of the details - // we provide an override for the error handling to ensure the widget gets the errors - // configure the setter instance with only the relevant widget instance properties - // NOTE: unless we hook into attr, or provide property setters for each property, - // we need to re-configure the ContentSetter with each use - var setter = this._contentSetter; - if(! (setter && setter instanceof dojo.html._ContentSetter)){ - setter = this._contentSetter = new dojo.html._ContentSetter({ - node: this.containerNode, - _onError: dojo.hitch(this, this._onError), - onContentError: dojo.hitch(this, function(e){ - // fires if a domfault occurs when we are appending this.errorMessage - // like for instance if domNode is a UL and we try append a DIV - var errMess = this.onContentError(e); - try{ - this.containerNode.innerHTML = errMess; - }catch(e){ - console.error('Fatal '+this.id+' could not change content due to '+e.message, e); - } - })/*, - _onError */ - }); - }; - - var setterParams = dojo.mixin({ - cleanContent: this.cleanContent, - extractContent: this.extractContent, - parseContent: this.parseOnLoad, - parserScope: this.parserScope, - startup: false, - dir: this.dir, - lang: this.lang - }, this._contentSetterParams || {}); - - setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams ); - - // setter params must be pulled afresh from the ContentPane each time - delete this._contentSetterParams; - - if(this.doLayout){ - this._checkIfSingleChild(); - } - - if(!isFakeContent){ - if(this._started){ - // Startup each top level child widget (and they will start their children, recursively) - this._startChildren(); - - // Call resize() on each of my child layout widgets, - // or resize() on my single child layout widget... - // either now (if I'm currently visible) or when I become visible - this._scheduleLayout(); - } - - this._onLoadHandler(cont); - } - }, - - _onError: function(type, err, consoleText){ - this.onLoadDeferred.errback(err); - - // shows user the string that is returned by on[type]Error - // override on[type]Error and return your own string to customize - var errText = this['on' + type + 'Error'].call(this, err); - if(consoleText){ - console.error(consoleText, err); - }else if(errText){// a empty string won't change current content - this._setContent(errText, true); - } - }, - - // EVENT's, should be overide-able - onLoad: function(data){ - // summary: - // Event hook, is called after everything is loaded and widgetified - // tags: - // callback - }, - - onUnload: function(){ - // summary: - // Event hook, is called before old content is cleared - // tags: - // callback - }, - - onDownloadStart: function(){ - // summary: - // Called before download starts. - // description: - // The string returned by this function will be the html - // that tells the user we are loading something. - // Override with your own function if you want to change text. - // tags: - // extension - return this.loadingMessage; - }, - - onContentError: function(/*Error*/ error){ - // summary: - // Called on DOM faults, require faults etc. in content. - // - // In order to display an error message in the pane, return - // the error message from this method, as an HTML string. - // - // By default (if this method is not overriden), it returns - // nothing, so the error message is just printed to the console. - // tags: - // extension - }, - - onDownloadError: function(/*Error*/ error){ - // summary: - // Called when download error occurs. - // - // In order to display an error message in the pane, return - // the error message from this method, as an HTML string. - // - // Default behavior (if this method is not overriden) is to display - // the error message inside the pane. - // tags: - // extension - return this.errorMessage; - }, - - onDownloadEnd: function(){ - // summary: - // Called when download is finished. - // tags: - // callback - } -}); - -} +//>>built +define("dijit/layout/ContentPane",["dojo/_base/kernel","dojo/_base/lang","../_Widget","./_ContentPaneResizeMixin","dojo/string","dojo/html","dojo/i18n!../nls/loading","dojo/_base/array","dojo/_base/declare","dojo/_base/Deferred","dojo/dom","dojo/dom-attr","dojo/_base/window","dojo/_base/xhr","dojo/i18n"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f){return _9("dijit.layout.ContentPane",[_3,_4],{href:"",content:"",extractContent:false,parseOnLoad:true,parserScope:_1._scopeName,preventCache:false,preload:false,refreshOnShow:false,loadingMessage:"<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",errorMessage:"<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",isLoaded:false,baseClass:"dijitContentPane",ioArgs:{},onLoadDeferred:null,_setTitleAttr:null,stopParser:true,template:false,create:function(_10,_11){if((!_10||!_10.template)&&_11&&!("href" in _10)&&!("content" in _10)){var df=_d.doc.createDocumentFragment();_11=_b.byId(_11);while(_11.firstChild){df.appendChild(_11.firstChild);}_10=_2.delegate(_10,{content:df});}this.inherited(arguments,[_10,_11]);},postMixInProperties:function(){this.inherited(arguments);var _12=_f.getLocalization("dijit","loading",this.lang);this.loadingMessage=_5.substitute(this.loadingMessage,_12);this.errorMessage=_5.substitute(this.errorMessage,_12);},buildRendering:function(){this.inherited(arguments);if(!this.containerNode){this.containerNode=this.domNode;}this.domNode.title="";if(!_c.get(this.domNode,"role")){this.domNode.setAttribute("role","group");}},startup:function(){this.inherited(arguments);if(this._contentSetter){_8.forEach(this._contentSetter.parseResults,function(obj){if(!obj._started&&!obj._destroyed&&_2.isFunction(obj.startup)){obj.startup();obj._started=true;}},this);}},setHref:function(_13){_1.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.","","2.0");return this.set("href",_13);},_setHrefAttr:function(_14){this.cancel();this.onLoadDeferred=new _a(_2.hitch(this,"cancel"));this.onLoadDeferred.addCallback(_2.hitch(this,"onLoad"));this._set("href",_14);if(this.preload||(this._created&&this._isShown())){this._load();}else{this._hrefChanged=true;}return this.onLoadDeferred;},setContent:function(_15){_1.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.","","2.0");this.set("content",_15);},_setContentAttr:function(_16){this._set("href","");this.cancel();this.onLoadDeferred=new _a(_2.hitch(this,"cancel"));if(this._created){this.onLoadDeferred.addCallback(_2.hitch(this,"onLoad"));}this._setContent(_16||"");this._isDownloaded=false;return this.onLoadDeferred;},_getContentAttr:function(){return this.containerNode.innerHTML;},cancel:function(){if(this._xhrDfd&&(this._xhrDfd.fired==-1)){this._xhrDfd.cancel();}delete this._xhrDfd;this.onLoadDeferred=null;},uninitialize:function(){if(this._beingDestroyed){this.cancel();}this.inherited(arguments);},destroyRecursive:function(_17){if(this._beingDestroyed){return;}this.inherited(arguments);},_onShow:function(){this.inherited(arguments);if(this.href){if(!this._xhrDfd&&(!this.isLoaded||this._hrefChanged||this.refreshOnShow)){return this.refresh();}}},refresh:function(){this.cancel();this.onLoadDeferred=new _a(_2.hitch(this,"cancel"));this.onLoadDeferred.addCallback(_2.hitch(this,"onLoad"));this._load();return this.onLoadDeferred;},_load:function(){this._setContent(this.onDownloadStart(),true);var _18=this;var _19={preventCache:(this.preventCache||this.refreshOnShow),url:this.href,handleAs:"text"};if(_2.isObject(this.ioArgs)){_2.mixin(_19,this.ioArgs);}var _1a=(this._xhrDfd=(this.ioMethod||_e.get)(_19));_1a.addCallback(function(_1b){try{_18._isDownloaded=true;_18._setContent(_1b,false);_18.onDownloadEnd();}catch(err){_18._onError("Content",err);}delete _18._xhrDfd;return _1b;});_1a.addErrback(function(err){if(!_1a.canceled){_18._onError("Download",err);}delete _18._xhrDfd;return err;});delete this._hrefChanged;},_onLoadHandler:function(_1c){this._set("isLoaded",true);try{this.onLoadDeferred.callback(_1c);}catch(e){console.error("Error "+this.widgetId+" running custom onLoad code: "+e.message);}},_onUnloadHandler:function(){this._set("isLoaded",false);try{this.onUnload();}catch(e){console.error("Error "+this.widgetId+" running custom onUnload code: "+e.message);}},destroyDescendants:function(_1d){if(this.isLoaded){this._onUnloadHandler();}var _1e=this._contentSetter;_8.forEach(this.getChildren(),function(_1f){if(_1f.destroyRecursive){_1f.destroyRecursive(_1d);}});if(_1e){_8.forEach(_1e.parseResults,function(_20){if(_20.destroyRecursive&&_20.domNode&&_20.domNode.parentNode==_d.body()){_20.destroyRecursive(_1d);}});delete _1e.parseResults;}if(!_1d){_6._emptyNode(this.containerNode);}delete this._singleChild;},_setContent:function(_21,_22){this.destroyDescendants();var _23=this._contentSetter;if(!(_23&&_23 instanceof _6._ContentSetter)){_23=this._contentSetter=new _6._ContentSetter({node:this.containerNode,_onError:_2.hitch(this,this._onError),onContentError:_2.hitch(this,function(e){var _24=this.onContentError(e);try{this.containerNode.innerHTML=_24;}catch(e){console.error("Fatal "+this.id+" could not change content due to "+e.message,e);}})});}var _25=_2.mixin({cleanContent:this.cleanContent,extractContent:this.extractContent,parseContent:!_21.domNode&&this.parseOnLoad,parserScope:this.parserScope,startup:false,dir:this.dir,lang:this.lang,textDir:this.textDir},this._contentSetterParams||{});_23.set((_2.isObject(_21)&&_21.domNode)?_21.domNode:_21,_25);delete this._contentSetterParams;if(this.doLayout){this._checkIfSingleChild();}if(!_22){if(this._started){delete this._started;this.startup();this._scheduleLayout();}this._onLoadHandler(_21);}},_onError:function(_26,err,_27){this.onLoadDeferred.errback(err);var _28=this["on"+_26+"Error"].call(this,err);if(_27){console.error(_27,err);}else{if(_28){this._setContent(_28,true);}}},onLoad:function(){},onUnload:function(){},onDownloadStart:function(){return this.loadingMessage;},onContentError:function(){},onDownloadError:function(){return this.errorMessage;},onDownloadEnd:function(){}});});
\ No newline at end of file diff --git a/lib/dijit/layout/ContentPane.js.uncompressed.js b/lib/dijit/layout/ContentPane.js.uncompressed.js new file mode 100644 index 000000000..eb57db2e0 --- /dev/null +++ b/lib/dijit/layout/ContentPane.js.uncompressed.js @@ -0,0 +1,611 @@ +define("dijit/layout/ContentPane", [ + "dojo/_base/kernel", // kernel.deprecated + "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject + "../_Widget", + "./_ContentPaneResizeMixin", + "dojo/string", // string.substitute + "dojo/html", // html._ContentSetter html._emptyNode + "dojo/i18n!../nls/loading", + "dojo/_base/array", // array.forEach + "dojo/_base/declare", // declare + "dojo/_base/Deferred", // Deferred + "dojo/dom", // dom.byId + "dojo/dom-attr", // domAttr.attr + "dojo/_base/window", // win.body win.doc.createDocumentFragment + "dojo/_base/xhr", // xhr.get + "dojo/i18n" // i18n.getLocalization +], function(kernel, lang, _Widget, _ContentPaneResizeMixin, string, html, nlsLoading, + array, declare, Deferred, dom, domAttr, win, xhr, i18n){ + +/*===== + var _Widget = dijit._Widget; + var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin; +=====*/ + +// module: +// dijit/layout/ContentPane +// summary: +// A widget containing an HTML fragment, specified inline +// or by uri. Fragment may include widgets. + + +return declare("dijit.layout.ContentPane", [_Widget, _ContentPaneResizeMixin], { + // summary: + // A widget containing an HTML fragment, specified inline + // or by uri. Fragment may include widgets. + // + // description: + // This widget embeds a document fragment in the page, specified + // either by uri, javascript generated markup or DOM reference. + // Any widgets within this content are instantiated and managed, + // but laid out according to the HTML structure. Unlike IFRAME, + // ContentPane embeds a document fragment as would be found + // inside the BODY tag of a full HTML document. It should not + // contain the HTML, HEAD, or BODY tags. + // For more advanced functionality with scripts and + // stylesheets, see dojox.layout.ContentPane. This widget may be + // used stand alone or as a base class for other widgets. + // ContentPane is useful as a child of other layout containers + // such as BorderContainer or TabContainer, but note that those + // widgets can contain any widget as a child. + // + // example: + // Some quick samples: + // To change the innerHTML: cp.set('content', '<b>new content</b>') + // + // Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection)) + // + // To do an ajax update: cp.set('href', url) + + // href: String + // The href of the content that displays now. + // Set this at construction if you want to load data externally when the + // pane is shown. (Set preload=true to load it immediately.) + // Changing href after creation doesn't have any effect; Use set('href', ...); + href: "", + + // content: String || DomNode || NodeList || dijit._Widget + // The innerHTML of the ContentPane. + // Note that the initialization parameter / argument to set("content", ...) + // can be a String, DomNode, Nodelist, or _Widget. + content: "", + + // extractContent: Boolean + // Extract visible content from inside of <body> .... </body>. + // I.e., strip <html> and <head> (and it's contents) from the href + extractContent: false, + + // parseOnLoad: Boolean + // Parse content and create the widgets, if any. + parseOnLoad: true, + + // parserScope: String + // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo, + // will search for data-dojo-type (or dojoType). For backwards compatibility + // reasons defaults to dojo._scopeName (which is "dojo" except when + // multi-version support is used, when it will be something like dojo16, dojo20, etc.) + parserScope: kernel._scopeName, + + // preventCache: Boolean + // Prevent caching of data from href's by appending a timestamp to the href. + preventCache: false, + + // preload: Boolean + // Force load of data on initialization even if pane is hidden. + preload: false, + + // refreshOnShow: Boolean + // Refresh (re-download) content when pane goes from hidden to shown + refreshOnShow: false, + + // loadingMessage: String + // Message that shows while downloading + loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>", + + // errorMessage: String + // Message that shows if an error occurs + errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>", + + // isLoaded: [readonly] Boolean + // True if the ContentPane has data in it, either specified + // during initialization (via href or inline content), or set + // via set('content', ...) / set('href', ...) + // + // False if it doesn't have any content, or if ContentPane is + // still in the process of downloading href. + isLoaded: false, + + baseClass: "dijitContentPane", + + /*====== + // ioMethod: dojo.xhrGet|dojo.xhrPost + // Function that should grab the content specified via href. + ioMethod: dojo.xhrGet, + ======*/ + + // ioArgs: Object + // Parameters to pass to xhrGet() request, for example: + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}"> + ioArgs: {}, + + // onLoadDeferred: [readonly] dojo.Deferred + // This is the `dojo.Deferred` returned by set('href', ...) and refresh(). + // Calling onLoadDeferred.addCallback() or addErrback() registers your + // callback to be called only once, when the prior set('href', ...) call or + // the initial href parameter to the constructor finishes loading. + // + // This is different than an onLoad() handler which gets called any time any href + // or content is loaded. + onLoadDeferred: null, + + // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify + // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the + // entire pane. + _setTitleAttr: null, + + // Flag to parser that I'll parse my contents, so it shouldn't. + stopParser: true, + + // template: [private] Boolean + // Flag from the parser that this ContentPane is inside a template + // so the contents are pre-parsed. + // (TODO: this declaration can be commented out in 2.0) + template: false, + + create: function(params, srcNodeRef){ + // Convert a srcNodeRef argument into a content parameter, so that the original contents are + // processed in the same way as contents set via set("content", ...), calling the parser etc. + // Avoid modifying original params object since that breaks NodeList instantiation, see #11906. + if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){ + var df = win.doc.createDocumentFragment(); + srcNodeRef = dom.byId(srcNodeRef); + while(srcNodeRef.firstChild){ + df.appendChild(srcNodeRef.firstChild); + } + params = lang.delegate(params, {content: df}); + } + this.inherited(arguments, [params, srcNodeRef]); + }, + + postMixInProperties: function(){ + this.inherited(arguments); + var messages = i18n.getLocalization("dijit", "loading", this.lang); + this.loadingMessage = string.substitute(this.loadingMessage, messages); + this.errorMessage = string.substitute(this.errorMessage, messages); + }, + + buildRendering: function(){ + this.inherited(arguments); + + // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work. + // For subclasses of ContentPane that do have a template, does nothing. + if(!this.containerNode){ + this.containerNode = this.domNode; + } + + // remove the title attribute so it doesn't show up when hovering + // over a node (TODO: remove in 2.0, no longer needed after #11490) + this.domNode.title = ""; + + if(!domAttr.get(this.domNode,"role")){ + this.domNode.setAttribute("role", "group"); + } + }, + + startup: function(){ + // summary: + // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects + + // This starts all the widgets + this.inherited(arguments); + + // And this catches stuff like dojo.dnd.Source + if(this._contentSetter){ + array.forEach(this._contentSetter.parseResults, function(obj){ + if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ + obj.startup(); + obj._started = true; + } + }, this); + } + }, + + setHref: function(/*String|Uri*/ href){ + // summary: + // Deprecated. Use set('href', ...) instead. + kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0"); + return this.set("href", href); + }, + _setHrefAttr: function(/*String|Uri*/ href){ + // summary: + // Hook so set("href", ...) works. + // description: + // Reset the (external defined) content of this pane and replace with new url + // Note: It delays the download until widget is shown if preload is false. + // href: + // url to the page you want to get, must be within the same domain as your mainpage + + // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...)) + this.cancel(); + + this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); + this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad")); + + this._set("href", href); + + // _setHrefAttr() is called during creation and by the user, after creation. + // Assuming preload == false, only in the second case do we actually load the URL; + // otherwise it's done in startup(), and only if this widget is shown. + if(this.preload || (this._created && this._isShown())){ + this._load(); + }else{ + // Set flag to indicate that href needs to be loaded the next time the + // ContentPane is made visible + this._hrefChanged = true; + } + + return this.onLoadDeferred; // Deferred + }, + + setContent: function(/*String|DomNode|Nodelist*/data){ + // summary: + // Deprecated. Use set('content', ...) instead. + kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0"); + this.set("content", data); + }, + _setContentAttr: function(/*String|DomNode|Nodelist*/data){ + // summary: + // Hook to make set("content", ...) work. + // Replaces old content with data content, include style classes from old content + // data: + // the new Content may be String, DomNode or NodeList + // + // if data is a NodeList (or an array of nodes) nodes are copied + // so you can import nodes from another document implicitly + + // clear href so we can't run refresh and clear content + // refresh should only work if we downloaded the content + this._set("href", ""); + + // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...)) + this.cancel(); + + // Even though user is just setting content directly, still need to define an onLoadDeferred + // because the _onLoadHandler() handler is still getting called from setContent() + this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); + if(this._created){ + // For back-compat reasons, call onLoad() for set('content', ...) + // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>) + // or as initialization parameter (ie: new ContentPane({content: ...}) + this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad")); + } + + this._setContent(data || ""); + + this._isDownloaded = false; // mark that content is from a set('content') not a set('href') + + return this.onLoadDeferred; // Deferred + }, + _getContentAttr: function(){ + // summary: + // Hook to make get("content") work + return this.containerNode.innerHTML; + }, + + cancel: function(){ + // summary: + // Cancels an in-flight download of content + if(this._xhrDfd && (this._xhrDfd.fired == -1)){ + this._xhrDfd.cancel(); + } + delete this._xhrDfd; // garbage collect + + this.onLoadDeferred = null; + }, + + uninitialize: function(){ + if(this._beingDestroyed){ + this.cancel(); + } + this.inherited(arguments); + }, + + destroyRecursive: function(/*Boolean*/ preserveDom){ + // summary: + // Destroy the ContentPane and its contents + + // if we have multiple controllers destroying us, bail after the first + if(this._beingDestroyed){ + return; + } + this.inherited(arguments); + }, + + _onShow: function(){ + // summary: + // Called when the ContentPane is made visible + // description: + // For a plain ContentPane, this is called on initialization, from startup(). + // If the ContentPane is a hidden pane of a TabContainer etc., then it's + // called whenever the pane is made visible. + // + // Does necessary processing, including href download and layout/resize of + // child widget(s) + + this.inherited(arguments); + + if(this.href){ + if(!this._xhrDfd && // if there's an href that isn't already being loaded + (!this.isLoaded || this._hrefChanged || this.refreshOnShow) + ){ + return this.refresh(); // If child has an href, promise that fires when the load is complete + } + } + }, + + refresh: function(){ + // summary: + // [Re]download contents of href and display + // description: + // 1. cancels any currently in-flight requests + // 2. posts "loading..." message + // 3. sends XHR to download new data + + // Cancel possible prior in-flight request + this.cancel(); + + this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); + this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad")); + this._load(); + return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete + }, + + _load: function(){ + // summary: + // Load/reload the href specified in this.href + + // display loading message + this._setContent(this.onDownloadStart(), true); + + var self = this; + var getArgs = { + preventCache: (this.preventCache || this.refreshOnShow), + url: this.href, + handleAs: "text" + }; + if(lang.isObject(this.ioArgs)){ + lang.mixin(getArgs, this.ioArgs); + } + + var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)); + + hand.addCallback(function(html){ + try{ + self._isDownloaded = true; + self._setContent(html, false); + self.onDownloadEnd(); + }catch(err){ + self._onError('Content', err); // onContentError + } + delete self._xhrDfd; + return html; + }); + + hand.addErrback(function(err){ + if(!hand.canceled){ + // show error message in the pane + self._onError('Download', err); // onDownloadError + } + delete self._xhrDfd; + return err; + }); + + // Remove flag saying that a load is needed + delete this._hrefChanged; + }, + + _onLoadHandler: function(data){ + // summary: + // This is called whenever new content is being loaded + this._set("isLoaded", true); + try{ + this.onLoadDeferred.callback(data); + }catch(e){ + console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message); + } + }, + + _onUnloadHandler: function(){ + // summary: + // This is called whenever the content is being unloaded + this._set("isLoaded", false); + try{ + this.onUnload(); + }catch(e){ + console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message); + } + }, + + destroyDescendants: function(/*Boolean*/ preserveDom){ + // summary: + // Destroy all the widgets inside the ContentPane and empty containerNode + + // Make sure we call onUnload (but only when the ContentPane has real content) + if(this.isLoaded){ + this._onUnloadHandler(); + } + + // Even if this.isLoaded == false there might still be a "Loading..." message + // to erase, so continue... + + // For historical reasons we need to delete all widgets under this.containerNode, + // even ones that the user has created manually. + var setter = this._contentSetter; + array.forEach(this.getChildren(), function(widget){ + if(widget.destroyRecursive){ + widget.destroyRecursive(preserveDom); + } + }); + if(setter){ + // Most of the widgets in setter.parseResults have already been destroyed, but + // things like Menu that have been moved to <body> haven't yet + array.forEach(setter.parseResults, function(widget){ + if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == win.body()){ + widget.destroyRecursive(preserveDom); + } + }); + delete setter.parseResults; + } + + // And then clear away all the DOM nodes + if(!preserveDom){ + html._emptyNode(this.containerNode); + } + + // Delete any state information we have about current contents + delete this._singleChild; + }, + + _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){ + // summary: + // Insert the content into the container node + + // first get rid of child widgets + this.destroyDescendants(); + + // html.set will take care of the rest of the details + // we provide an override for the error handling to ensure the widget gets the errors + // configure the setter instance with only the relevant widget instance properties + // NOTE: unless we hook into attr, or provide property setters for each property, + // we need to re-configure the ContentSetter with each use + var setter = this._contentSetter; + if(! (setter && setter instanceof html._ContentSetter)){ + setter = this._contentSetter = new html._ContentSetter({ + node: this.containerNode, + _onError: lang.hitch(this, this._onError), + onContentError: lang.hitch(this, function(e){ + // fires if a domfault occurs when we are appending this.errorMessage + // like for instance if domNode is a UL and we try append a DIV + var errMess = this.onContentError(e); + try{ + this.containerNode.innerHTML = errMess; + }catch(e){ + console.error('Fatal '+this.id+' could not change content due to '+e.message, e); + } + })/*, + _onError */ + }); + } + + var setterParams = lang.mixin({ + cleanContent: this.cleanContent, + extractContent: this.extractContent, + parseContent: !cont.domNode && this.parseOnLoad, + parserScope: this.parserScope, + startup: false, + dir: this.dir, + lang: this.lang, + textDir: this.textDir + }, this._contentSetterParams || {}); + + setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams ); + + // setter params must be pulled afresh from the ContentPane each time + delete this._contentSetterParams; + + if(this.doLayout){ + this._checkIfSingleChild(); + } + + if(!isFakeContent){ + if(this._started){ + // Startup each top level child widget (and they will start their children, recursively) + delete this._started; + this.startup(); + + // Call resize() on each of my child layout widgets, + // or resize() on my single child layout widget... + // either now (if I'm currently visible) or when I become visible + this._scheduleLayout(); + } + + this._onLoadHandler(cont); + } + }, + + _onError: function(type, err, consoleText){ + this.onLoadDeferred.errback(err); + + // shows user the string that is returned by on[type]Error + // override on[type]Error and return your own string to customize + var errText = this['on' + type + 'Error'].call(this, err); + if(consoleText){ + console.error(consoleText, err); + }else if(errText){// a empty string won't change current content + this._setContent(errText, true); + } + }, + + // EVENT's, should be overide-able + onLoad: function(/*===== data =====*/){ + // summary: + // Event hook, is called after everything is loaded and widgetified + // tags: + // callback + }, + + onUnload: function(){ + // summary: + // Event hook, is called before old content is cleared + // tags: + // callback + }, + + onDownloadStart: function(){ + // summary: + // Called before download starts. + // description: + // The string returned by this function will be the html + // that tells the user we are loading something. + // Override with your own function if you want to change text. + // tags: + // extension + return this.loadingMessage; + }, + + onContentError: function(/*Error*/ /*===== error =====*/){ + // summary: + // Called on DOM faults, require faults etc. in content. + // + // In order to display an error message in the pane, return + // the error message from this method, as an HTML string. + // + // By default (if this method is not overriden), it returns + // nothing, so the error message is just printed to the console. + // tags: + // extension + }, + + onDownloadError: function(/*Error*/ /*===== error =====*/){ + // summary: + // Called when download error occurs. + // + // In order to display an error message in the pane, return + // the error message from this method, as an HTML string. + // + // Default behavior (if this method is not overriden) is to display + // the error message inside the pane. + // tags: + // extension + return this.errorMessage; + }, + + onDownloadEnd: function(){ + // summary: + // Called when download is finished. + // tags: + // callback + } +}); + +}); diff --git a/lib/dijit/layout/LayoutContainer.js b/lib/dijit/layout/LayoutContainer.js index 11204346f..bc4f3a618 100644 --- a/lib/dijit/layout/LayoutContainer.js +++ b/lib/dijit/layout/LayoutContainer.js @@ -1,86 +1,2 @@ -/* - 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.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.LayoutContainer"] = true; -dojo.provide("dijit.layout.LayoutContainer"); -dojo.require("dijit.layout._LayoutWidget"); - - -dojo.declare("dijit.layout.LayoutContainer", - dijit.layout._LayoutWidget, - { - // summary: - // Deprecated. Use `dijit.layout.BorderContainer` instead. - // - // description: - // Provides Delphi-style panel layout semantics. - // - // A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"), - // that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client". - // It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box, - // and then it takes the child marked "client" and puts it into the remaining space in the middle. - // - // Left/right positioning is similar to CSS's "float: left" and "float: right", - // and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such - // CSS. - // - // Note that there can only be one client element, but there can be multiple left, right, top, - // or bottom elements. - // - // example: - // | <style> - // | html, body{ height: 100%; width: 100%; } - // | </style> - // | <div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%"> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div> - // | <div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div> - // | </div> - // - // Lays out each child in the natural order the children occur in. - // Basically each child is laid out into the "remaining space", where "remaining space" is initially - // the content area of this widget, but is reduced to a smaller rectangle each time a child is added. - // tags: - // deprecated - - baseClass: "dijitLayoutContainer", - - constructor: function(){ - dojo.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0); - }, - - layout: function(){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - this.inherited(arguments); - if(this._started){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - } - }, - - removeChild: function(/*dijit._Widget*/ widget){ - this.inherited(arguments); - if(this._started){ - dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren()); - } - } -}); - -// This argument can be specified for the children of a LayoutContainer. -// Since any widget can be specified as a LayoutContainer child, mix it -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // layoutAlign: String - // "none", "left", "right", "bottom", "top", and "client". - // See the LayoutContainer description for details on this parameter. - layoutAlign: 'none' -}); - -} +//>>built +define("dijit/layout/LayoutContainer",["dojo/_base/kernel","dojo/_base/lang","dojo/_base/declare","../_WidgetBase","./_LayoutWidget","./utils"],function(_1,_2,_3,_4,_5,_6){_2.extend(_4,{layoutAlign:"none"});return _3("dijit.layout.LayoutContainer",_5,{baseClass:"dijitLayoutContainer",constructor:function(){_1.deprecated("dijit.layout.LayoutContainer is deprecated","use BorderContainer instead",2);},layout:function(){_6.layoutChildren(this.domNode,this._contentBox,this.getChildren());},addChild:function(_7,_8){this.inherited(arguments);if(this._started){_6.layoutChildren(this.domNode,this._contentBox,this.getChildren());}},removeChild:function(_9){this.inherited(arguments);if(this._started){_6.layoutChildren(this.domNode,this._contentBox,this.getChildren());}}});});
\ No newline at end of file diff --git a/lib/dijit/layout/LayoutContainer.js.uncompressed.js b/lib/dijit/layout/LayoutContainer.js.uncompressed.js new file mode 100644 index 000000000..0881ba5a1 --- /dev/null +++ b/lib/dijit/layout/LayoutContainer.js.uncompressed.js @@ -0,0 +1,91 @@ +define("dijit/layout/LayoutContainer", [ + "dojo/_base/kernel", // kernel.deprecated + "dojo/_base/lang", + "dojo/_base/declare", // declare + "../_WidgetBase", + "./_LayoutWidget", + "./utils" // layoutUtils.layoutChildren +], function(kernel, lang, declare, _WidgetBase, _LayoutWidget, layoutUtils){ + +/*===== + var _WidgetBase = dijit._WidgetBase; + var _LayoutWidget = dijit.layout._LayoutWidget; +=====*/ + +// module: +// dijit/layout/LayoutContainer +// summary: +// Deprecated. Use `dijit.layout.BorderContainer` instead. + + +// This argument can be specified for the children of a LayoutContainer. +// Since any widget can be specified as a LayoutContainer child, mix it +// into the base widget class. (This is a hack, but it's effective.) +lang.extend(_WidgetBase, { + // layoutAlign: String + // "none", "left", "right", "bottom", "top", and "client". + // See the LayoutContainer description for details on this parameter. + layoutAlign: 'none' +}); + +return declare("dijit.layout.LayoutContainer", _LayoutWidget, { + // summary: + // Deprecated. Use `dijit.layout.BorderContainer` instead. + // + // description: + // Provides Delphi-style panel layout semantics. + // + // A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"), + // that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client". + // It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box, + // and then it takes the child marked "client" and puts it into the remaining space in the middle. + // + // Left/right positioning is similar to CSS's "float: left" and "float: right", + // and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such + // CSS. + // + // Note that there can only be one client element, but there can be multiple left, right, top, + // or bottom elements. + // + // example: + // | <style> + // | html, body{ height: 100%; width: 100%; } + // | </style> + // | <div data-dojo-type="dijit.layout.LayoutContainer" style="width: 100%; height: 100%"> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'top'">header text</div> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'left'" style="width: 200px;">table of contents</div> + // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="layoutAlign: 'client'">client area</div> + // | </div> + // + // Lays out each child in the natural order the children occur in. + // Basically each child is laid out into the "remaining space", where "remaining space" is initially + // the content area of this widget, but is reduced to a smaller rectangle each time a child is added. + // tags: + // deprecated + + baseClass: "dijitLayoutContainer", + + constructor: function(){ + kernel.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0); + }, + + layout: function(){ + layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren()); + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + this.inherited(arguments); + if(this._started){ + layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren()); + } + }, + + removeChild: function(/*dijit._Widget*/ widget){ + this.inherited(arguments); + if(this._started){ + layoutUtils.layoutChildren(this.domNode, this._contentBox, this.getChildren()); + } + } +}); + +}); diff --git a/lib/dijit/layout/LinkPane.js b/lib/dijit/layout/LinkPane.js index 34e2d92bb..b3714bb04 100644 --- a/lib/dijit/layout/LinkPane.js +++ b/lib/dijit/layout/LinkPane.js @@ -1,51 +1,2 @@ -/* - 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.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.LinkPane"] = true; -dojo.provide("dijit.layout.LinkPane"); -dojo.require("dijit.layout.ContentPane"); -dojo.require("dijit._Templated"); - - -dojo.declare("dijit.layout.LinkPane", - [dijit.layout.ContentPane, dijit._Templated], - { - // summary: - // A ContentPane with an href where (when declared in markup) - // the title is specified as innerHTML rather than as a title attribute. - // description: - // LinkPane is just a ContentPane that is declared in markup similarly - // to an anchor. The anchor's body (the words between `<a>` and `</a>`) - // become the title of the widget (used for TabContainer, AccordionContainer, etc.) - // example: - // | <a href="foo.html">my title</a> - - // I'm using a template because the user may specify the input as - // <a href="foo.html">title</a>, in which case we need to get rid of the - // <a> because we don't want a link. - templateString: '<div class="dijitLinkPane" dojoAttachPoint="containerNode"></div>', - - postMixInProperties: function(){ - // If user has specified node contents, they become the title - // (the link must be plain text) - if(this.srcNodeRef){ - this.title += this.srcNodeRef.innerHTML; - } - this.inherited(arguments); - }, - - _fillContent: function(/*DomNode*/ source){ - // Overrides _Templated._fillContent(). - - // _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node, - // but in our case the srcNodeRef innerHTML is the title, so shouldn't be - // copied - } -}); - -} +//>>built +define("dijit/layout/LinkPane",["./ContentPane","../_TemplatedMixin","dojo/_base/declare"],function(_1,_2,_3){return _3("dijit.layout.LinkPane",[_1,_2],{templateString:"<div class=\"dijitLinkPane\" data-dojo-attach-point=\"containerNode\"></div>",postMixInProperties:function(){if(this.srcNodeRef){this.title+=this.srcNodeRef.innerHTML;}this.inherited(arguments);},_fillContent:function(){}});});
\ No newline at end of file diff --git a/lib/dijit/layout/LinkPane.js.uncompressed.js b/lib/dijit/layout/LinkPane.js.uncompressed.js new file mode 100644 index 000000000..f70607185 --- /dev/null +++ b/lib/dijit/layout/LinkPane.js.uncompressed.js @@ -0,0 +1,52 @@ +define("dijit/layout/LinkPane", [ + "./ContentPane", + "../_TemplatedMixin", + "dojo/_base/declare" // declare +], function(ContentPane, _TemplatedMixin, declare){ + +/*===== + var _TemplatedMixin = dijit._TemplatedMixin; + var ContentPane = dijit.layout.ContentPane; +=====*/ + + // module: + // dijit/layout/LinkPane + // summary: + // A ContentPane with an href where (when declared in markup) + // the title is specified as innerHTML rather than as a title attribute. + + + return declare("dijit.layout.LinkPane", [ContentPane, _TemplatedMixin], { + // summary: + // A ContentPane with an href where (when declared in markup) + // the title is specified as innerHTML rather than as a title attribute. + // description: + // LinkPane is just a ContentPane that is declared in markup similarly + // to an anchor. The anchor's body (the words between `<a>` and `</a>`) + // become the title of the widget (used for TabContainer, AccordionContainer, etc.) + // example: + // | <a href="foo.html">my title</a> + + // I'm using a template because the user may specify the input as + // <a href="foo.html">title</a>, in which case we need to get rid of the + // <a> because we don't want a link. + templateString: '<div class="dijitLinkPane" data-dojo-attach-point="containerNode"></div>', + + postMixInProperties: function(){ + // If user has specified node contents, they become the title + // (the link must be plain text) + if(this.srcNodeRef){ + this.title += this.srcNodeRef.innerHTML; + } + this.inherited(arguments); + }, + + _fillContent: function(){ + // Overrides _Templated._fillContent(). + + // _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node, + // but in our case the srcNodeRef innerHTML is the title, so shouldn't be + // copied + } + }); +}); diff --git a/lib/dijit/layout/ScrollingTabController.js b/lib/dijit/layout/ScrollingTabController.js index a91c526de..aa4bc7626 100644 --- a/lib/dijit/layout/ScrollingTabController.js +++ b/lib/dijit/layout/ScrollingTabController.js @@ -1,483 +1,2 @@ -/* - 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.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.ScrollingTabController"] = true; -dojo.provide("dijit.layout.ScrollingTabController"); -dojo.require("dijit.layout.TabController"); -dojo.require("dijit.Menu"); -dojo.require("dijit.form.Button"); -dojo.require("dijit._HasDropDown"); - - -dojo.declare("dijit.layout.ScrollingTabController", - dijit.layout.TabController, - { - // summary: - // Set of tabs with left/right arrow keys and a menu to switch between tabs not - // all fitting on a single row. - // Works only for horizontal tabs (either above or below the content, not to the left - // or right). - // tags: - // private - - templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" containerId=\"${containerId}\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdropDownPosition=\"below-alt, above-alt\"\n\t\t\tdojoAttachPoint=\"_menuBtn\" showLabel=\"false\">▼</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\" iconClass=\"dijitTabStripSlideLeftIcon\"\n\t\t\tdojoAttachPoint=\"_leftBtn\" dojoAttachEvent=\"onClick: doSlideLeft\" showLabel=\"false\">◀</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\" iconClass=\"dijitTabStripSlideRightIcon\"\n\t\t\tdojoAttachPoint=\"_rightBtn\" dojoAttachEvent=\"onClick: doSlideRight\" showLabel=\"false\">▶</div>\n\t<div class='dijitTabListWrapper' dojoAttachPoint='tablistWrapper'>\n\t\t<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"), - - // useMenu: [const] Boolean - // True if a menu should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useMenu: true, - - // useSlider: [const] Boolean - // True if a slider should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useSlider: true, - - // tabStripClass: [const] String - // The css class to apply to the tab strip, if it is visible. - tabStripClass: "", - - widgetsInTemplate: true, - - // _minScroll: Number - // The distance in pixels from the edge of the tab strip which, - // if a scroll animation is less than, forces the scroll to - // go all the way to the left/right. - _minScroll: 5, - - attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { - "class": "containerNode" - }), - - buildRendering: function(){ - this.inherited(arguments); - var n = this.domNode; - - this.scrollNode = this.tablistWrapper; - this._initButtons(); - - if(!this.tabStripClass){ - this.tabStripClass = "dijitTabContainer" + - this.tabPosition.charAt(0).toUpperCase() + - this.tabPosition.substr(1).replace(/-.*/, "") + - "None"; - dojo.addClass(n, "tabStrip-disabled") - } - - dojo.addClass(this.tablistWrapper, this.tabStripClass); - }, - - onStartup: function(){ - this.inherited(arguments); - - // Do not show the TabController until the related - // StackController has added it's children. This gives - // a less visually jumpy instantiation. - dojo.style(this.domNode, "visibility", "visible"); - this._postStartup = true; - }, - - onAddChild: function(page, insertIndex){ - this.inherited(arguments); - - // changes to the tab button label or iconClass will have changed the width of the - // buttons, so do a resize - dojo.forEach(["label", "iconClass"], function(attr){ - this.pane2watches[page.id].push( - this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){ - if(this._postStartup && this._dim){ - this.resize(this._dim); - } - })) - ); - }, this); - - // Increment the width of the wrapper when a tab is added - // This makes sure that the buttons never wrap. - // The value 200 is chosen as it should be bigger than most - // Tab button widths. - dojo.style(this.containerNode, "width", - (dojo.style(this.containerNode, "width") + 200) + "px"); - }, - - onRemoveChild: function(page, insertIndex){ - // null out _selectedTab because we are about to delete that dom node - var button = this.pane2button[page.id]; - if(this._selectedTab === button.domNode){ - this._selectedTab = null; - } - - this.inherited(arguments); - }, - - _initButtons: function(){ - // summary: - // Creates the buttons used to scroll to view tabs that - // may not be visible if the TabContainer is too narrow. - - // Make a list of the buttons to display when the tab labels become - // wider than the TabContainer, and hide the other buttons. - // Also gets the total width of the displayed buttons. - this._btnWidth = 0; - this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){ - if((this.useMenu && btn == this._menuBtn.domNode) || - (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){ - this._btnWidth += dojo._getMarginSize(btn).w; - return true; - }else{ - dojo.style(btn, "display", "none"); - return false; - } - }, this); - }, - - _getTabsWidth: function(){ - var children = this.getChildren(); - if(children.length){ - var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode, - rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode; - return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft; - }else{ - return 0; - } - }, - - _enableBtn: function(width){ - // summary: - // Determines if the tabs are wider than the width of the TabContainer, and - // thus that we need to display left/right/menu navigation buttons. - var tabsWidth = this._getTabsWidth(); - width = width || dojo.style(this.scrollNode, "width"); - return tabsWidth > 0 && width < tabsWidth; - }, - - resize: function(dim){ - // summary: - // Hides or displays the buttons used to scroll the tab list and launch the menu - // that selects tabs. - - if(this.domNode.offsetWidth == 0){ - return; - } - - // Save the dimensions to be used when a child is renamed. - this._dim = dim; - - // Set my height to be my natural height (tall enough for one row of tab labels), - // and my content-box width based on margin-box width specified in dim parameter. - // But first reset scrollNode.height in case it was set by layoutChildren() call - // in a previous run of this method. - this.scrollNode.style.height = "auto"; - this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w}); - this._contentBox.h = this.scrollNode.offsetHeight; - dojo.contentBox(this.domNode, this._contentBox); - - // Show/hide the left/right/menu navigation buttons depending on whether or not they - // are needed. - var enable = this._enableBtn(this._contentBox.w); - this._buttons.style("display", enable ? "" : "none"); - - // Position and size the navigation buttons and the tablist - this._leftBtn.layoutAlign = "left"; - this._rightBtn.layoutAlign = "right"; - this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left"; - dijit.layout.layoutChildren(this.domNode, this._contentBox, - [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]); - - // set proper scroll so that selected tab is visible - if(this._selectedTab){ - if(this._anim && this._anim.status() == "playing"){ - this._anim.stop(); - } - var w = this.scrollNode, - sl = this._convertToScrollLeft(this._getScrollForSelectedTab()); - w.scrollLeft = sl; - } - - // Enable/disabled left right buttons depending on whether or not user can scroll to left or right - this._setButtonClass(this._getScroll()); - - this._postResize = true; - - // Return my size so layoutChildren() can use it. - // Also avoids IE9 layout glitch on browser resize when scroll buttons present - return {h: this._contentBox.h, w: dim.w}; - }, - - _getScroll: function(){ - // summary: - // Returns the current scroll of the tabs where 0 means - // "scrolled all the way to the left" and some positive number, based on # - // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right" - var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft : - dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width") - + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft; - return sl; - }, - - _convertToScrollLeft: function(val){ - // summary: - // Given a scroll value where 0 means "scrolled all the way to the left" - // and some positive number, based on # of pixels of possible scroll (ex: 1000) - // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft - // to achieve that scroll. - // - // This method is to adjust for RTL funniness in various browsers and versions. - if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){ - return val; - }else{ - var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width"); - return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll); - } - }, - - onSelectChild: function(/*dijit._Widget*/ page){ - // summary: - // Smoothly scrolls to a tab when it is selected. - - var tab = this.pane2button[page.id]; - if(!tab || !page){return;} - - // Scroll to the selected tab, except on startup, when scrolling is handled in resize() - var node = tab.domNode; - if(this._postResize && node != this._selectedTab){ - this._selectedTab = node; - - var sl = this._getScroll(); - - if(sl > node.offsetLeft || - sl + dojo.style(this.scrollNode, "width") < - node.offsetLeft + dojo.style(node, "width")){ - this.createSmoothScroll().play(); - } - } - - this.inherited(arguments); - }, - - _getScrollBounds: function(){ - // summary: - // Returns the minimum and maximum scroll setting to show the leftmost and rightmost - // tabs (respectively) - var children = this.getChildren(), - scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px - containerWidth = dojo.style(this.containerNode, "width"), // 50,000px - maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible - tabsWidth = this._getTabsWidth(); - - if(children.length && tabsWidth > scrollNodeWidth){ - // Scrolling should happen - return { - min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft, - max: this.isLeftToRight() ? - (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth : - maxPossibleScroll - }; - }else{ - // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir) - var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll; - return { - min: onlyScrollPosition, - max: onlyScrollPosition - }; - } - }, - - _getScrollForSelectedTab: function(){ - // summary: - // Returns the scroll value setting so that the selected tab - // will appear in the center - var w = this.scrollNode, - n = this._selectedTab, - scrollNodeWidth = dojo.style(this.scrollNode, "width"), - scrollBounds = this._getScrollBounds(); - - // TODO: scroll minimal amount (to either right or left) so that - // selected tab is fully visible, and just return if it's already visible? - var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2; - pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max); - - // TODO: - // If scrolling close to the left side or right side, scroll - // all the way to the left or right. See this._minScroll. - // (But need to make sure that doesn't scroll the tab out of view...) - return pos; - }, - - createSmoothScroll: function(x){ - // summary: - // Creates a dojo._Animation object that smoothly scrolls the tab list - // either to a fixed horizontal pixel value, or to the selected tab. - // description: - // If an number argument is passed to the function, that horizontal - // pixel position is scrolled to. Otherwise the currently selected - // tab is scrolled to. - // x: Integer? - // An optional pixel value to scroll to, indicating distance from left. - - // Calculate position to scroll to - if(arguments.length > 0){ - // position specified by caller, just make sure it's within bounds - var scrollBounds = this._getScrollBounds(); - x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max); - }else{ - // scroll to center the current tab - x = this._getScrollForSelectedTab(); - } - - if(this._anim && this._anim.status() == "playing"){ - this._anim.stop(); - } - - var self = this, - w = this.scrollNode, - anim = new dojo._Animation({ - beforeBegin: function(){ - if(this.curve){ delete this.curve; } - var oldS = w.scrollLeft, - newS = self._convertToScrollLeft(x); - anim.curve = new dojo._Line(oldS, newS); - }, - onAnimate: function(val){ - w.scrollLeft = val; - } - }); - this._anim = anim; - - // Disable/enable left/right buttons according to new scroll position - this._setButtonClass(x); - - return anim; // dojo._Animation - }, - - _getBtnNode: function(/*Event*/ e){ - // summary: - // Gets a button DOM node from a mouse click event. - // e: - // The mouse click event. - var n = e.target; - while(n && !dojo.hasClass(n, "tabStripButton")){ - n = n.parentNode; - } - return n; - }, - - doSlideRight: function(/*Event*/ e){ - // summary: - // Scrolls the menu to the right. - // e: - // The mouse click event. - this.doSlide(1, this._getBtnNode(e)); - }, - - doSlideLeft: function(/*Event*/ e){ - // summary: - // Scrolls the menu to the left. - // e: - // The mouse click event. - this.doSlide(-1,this._getBtnNode(e)); - }, - - doSlide: function(/*Number*/ direction, /*DomNode*/ node){ - // summary: - // Scrolls the tab list to the left or right by 75% of the widget width. - // direction: - // If the direction is 1, the widget scrolls to the right, if it is - // -1, it scrolls to the left. - - if(node && dojo.hasClass(node, "dijitTabDisabled")){return;} - - var sWidth = dojo.style(this.scrollNode, "width"); - var d = (sWidth * 0.75) * direction; - - var to = this._getScroll() + d; - - this._setButtonClass(to); - - this.createSmoothScroll(to).play(); - }, - - _setButtonClass: function(/*Number*/ scroll){ - // summary: - // Disables the left scroll button if the tabs are scrolled all the way to the left, - // or the right scroll button in the opposite case. - // scroll: Integer - // amount of horizontal scroll - - var scrollBounds = this._getScrollBounds(); - this._leftBtn.set("disabled", scroll <= scrollBounds.min); - this._rightBtn.set("disabled", scroll >= scrollBounds.max); - } -}); - - -dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, { - baseClass: "dijitTab tabStripButton", - - templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t\t<span dojoAttachPoint=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>\n"), - - // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be - // able to tab to the left/right/menu buttons - tabIndex: "", - - // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it - // either (this override avoids focus() call in FormWidget.js) - isFocusable: function(){ return false; } -}); - -dojo.declare("dijit.layout._ScrollingTabControllerButton", - [dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]); - -dojo.declare( - "dijit.layout._ScrollingTabControllerMenuButton", - [dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin], -{ - // id of the TabContainer itself - containerId: "", - - // -1 so user can't tab into the button, but so that button can still be focused programatically. - // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash. - tabIndex: "-1", - - isLoaded: function(){ - // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed - return false; - }, - - loadDropDown: function(callback){ - this.dropDown = new dijit.Menu({ - id: this.containerId + "_menu", - dir: this.dir, - lang: this.lang - }); - var container = dijit.byId(this.containerId); - dojo.forEach(container.getChildren(), function(page){ - var menuItem = new dijit.MenuItem({ - id: page.id + "_stcMi", - label: page.title, - iconClass: page.iconClass, - dir: page.dir, - lang: page.lang, - onClick: function(){ - container.selectChild(page); - } - }); - this.dropDown.addChild(menuItem); - }, this); - callback(); - }, - - closeDropDown: function(/*Boolean*/ focus){ - this.inherited(arguments); - if(this.dropDown){ - this.dropDown.destroyRecursive(); - delete this.dropDown; - } - } -}); - -} +//>>built +require({cache:{"url:dijit/layout/templates/ScrollingTabController.html":"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">◀</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>","url:dijit/layout/templates/_ScrollingTabControllerButton.html":"<div data-dojo-attach-event=\"onclick:_onClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>"}});define("dijit/layout/ScrollingTabController",["dojo/_base/array","dojo/_base/declare","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/_base/fx","dojo/_base/lang","dojo/query","dojo/_base/sniff","../registry","dojo/text!./templates/ScrollingTabController.html","dojo/text!./templates/_ScrollingTabControllerButton.html","./TabController","./utils","../_WidgetsInTemplateMixin","../Menu","../MenuItem","../form/Button","../_HasDropDown","dojo/NodeList-dom"],function(_1,_2,_3,_4,_5,fx,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f,_10,_11,_12){var _13=_2("dijit.layout.ScrollingTabController",[_c,_e],{baseClass:"dijitTabController dijitScrollingTabController",templateString:_a,useMenu:true,useSlider:true,tabStripClass:"",widgetsInTemplate:true,_minScroll:5,_setClassAttr:{node:"containerNode",type:"class"},buildRendering:function(){this.inherited(arguments);var n=this.domNode;this.scrollNode=this.tablistWrapper;this._initButtons();if(!this.tabStripClass){this.tabStripClass="dijitTabContainer"+this.tabPosition.charAt(0).toUpperCase()+this.tabPosition.substr(1).replace(/-.*/,"")+"None";_3.add(n,"tabStrip-disabled");}_3.add(this.tablistWrapper,this.tabStripClass);},onStartup:function(){this.inherited(arguments);_5.set(this.domNode,"visibility","");this._postStartup=true;},onAddChild:function(_14,_15){this.inherited(arguments);_1.forEach(["label","iconClass"],function(_16){this.pane2watches[_14.id].push(this.pane2button[_14.id].watch(_16,_6.hitch(this,function(){if(this._postStartup&&this._dim){this.resize(this._dim);}})));},this);_5.set(this.containerNode,"width",(_5.get(this.containerNode,"width")+200)+"px");},onRemoveChild:function(_17,_18){var _19=this.pane2button[_17.id];if(this._selectedTab===_19.domNode){this._selectedTab=null;}this.inherited(arguments);},_initButtons:function(){this._btnWidth=0;this._buttons=_7("> .tabStripButton",this.domNode).filter(function(btn){if((this.useMenu&&btn==this._menuBtn.domNode)||(this.useSlider&&(btn==this._rightBtn.domNode||btn==this._leftBtn.domNode))){this._btnWidth+=_4.getMarginSize(btn).w;return true;}else{_5.set(btn,"display","none");return false;}},this);},_getTabsWidth:function(){var _1a=this.getChildren();if(_1a.length){var _1b=_1a[this.isLeftToRight()?0:_1a.length-1].domNode,_1c=_1a[this.isLeftToRight()?_1a.length-1:0].domNode;return _1c.offsetLeft+_5.get(_1c,"width")-_1b.offsetLeft;}else{return 0;}},_enableBtn:function(_1d){var _1e=this._getTabsWidth();_1d=_1d||_5.get(this.scrollNode,"width");return _1e>0&&_1d<_1e;},resize:function(dim){this._dim=dim;this.scrollNode.style.height="auto";var cb=this._contentBox=_d.marginBox2contentBox(this.domNode,{h:0,w:dim.w});cb.h=this.scrollNode.offsetHeight;_4.setContentSize(this.domNode,cb);var _1f=this._enableBtn(this._contentBox.w);this._buttons.style("display",_1f?"":"none");this._leftBtn.layoutAlign="left";this._rightBtn.layoutAlign="right";this._menuBtn.layoutAlign=this.isLeftToRight()?"right":"left";_d.layoutChildren(this.domNode,this._contentBox,[this._menuBtn,this._leftBtn,this._rightBtn,{domNode:this.scrollNode,layoutAlign:"client"}]);if(this._selectedTab){if(this._anim&&this._anim.status()=="playing"){this._anim.stop();}this.scrollNode.scrollLeft=this._convertToScrollLeft(this._getScrollForSelectedTab());}this._setButtonClass(this._getScroll());this._postResize=true;return {h:this._contentBox.h,w:dim.w};},_getScroll:function(){return (this.isLeftToRight()||_8("ie")<8||(_8("ie")&&_8("quirks"))||_8("webkit"))?this.scrollNode.scrollLeft:_5.get(this.containerNode,"width")-_5.get(this.scrollNode,"width")+(_8("ie")==8?-1:1)*this.scrollNode.scrollLeft;},_convertToScrollLeft:function(val){if(this.isLeftToRight()||_8("ie")<8||(_8("ie")&&_8("quirks"))||_8("webkit")){return val;}else{var _20=_5.get(this.containerNode,"width")-_5.get(this.scrollNode,"width");return (_8("ie")==8?-1:1)*(val-_20);}},onSelectChild:function(_21){var tab=this.pane2button[_21.id];if(!tab||!_21){return;}var _22=tab.domNode;if(_22!=this._selectedTab){this._selectedTab=_22;if(this._postResize){var sl=this._getScroll();if(sl>_22.offsetLeft||sl+_5.get(this.scrollNode,"width")<_22.offsetLeft+_5.get(_22,"width")){this.createSmoothScroll().play();}}}this.inherited(arguments);},_getScrollBounds:function(){var _23=this.getChildren(),_24=_5.get(this.scrollNode,"width"),_25=_5.get(this.containerNode,"width"),_26=_25-_24,_27=this._getTabsWidth();if(_23.length&&_27>_24){return {min:this.isLeftToRight()?0:_23[_23.length-1].domNode.offsetLeft,max:this.isLeftToRight()?(_23[_23.length-1].domNode.offsetLeft+_5.get(_23[_23.length-1].domNode,"width"))-_24:_26};}else{var _28=this.isLeftToRight()?0:_26;return {min:_28,max:_28};}},_getScrollForSelectedTab:function(){var w=this.scrollNode,n=this._selectedTab,_29=_5.get(this.scrollNode,"width"),_2a=this._getScrollBounds();var pos=(n.offsetLeft+_5.get(n,"width")/2)-_29/2;pos=Math.min(Math.max(pos,_2a.min),_2a.max);return pos;},createSmoothScroll:function(x){if(arguments.length>0){var _2b=this._getScrollBounds();x=Math.min(Math.max(x,_2b.min),_2b.max);}else{x=this._getScrollForSelectedTab();}if(this._anim&&this._anim.status()=="playing"){this._anim.stop();}var _2c=this,w=this.scrollNode,_2d=new fx.Animation({beforeBegin:function(){if(this.curve){delete this.curve;}var _2e=w.scrollLeft,_2f=_2c._convertToScrollLeft(x);_2d.curve=new fx._Line(_2e,_2f);},onAnimate:function(val){w.scrollLeft=val;}});this._anim=_2d;this._setButtonClass(x);return _2d;},_getBtnNode:function(e){var n=e.target;while(n&&!_3.contains(n,"tabStripButton")){n=n.parentNode;}return n;},doSlideRight:function(e){this.doSlide(1,this._getBtnNode(e));},doSlideLeft:function(e){this.doSlide(-1,this._getBtnNode(e));},doSlide:function(_30,_31){if(_31&&_3.contains(_31,"dijitTabDisabled")){return;}var _32=_5.get(this.scrollNode,"width");var d=(_32*0.75)*_30;var to=this._getScroll()+d;this._setButtonClass(to);this.createSmoothScroll(to).play();},_setButtonClass:function(_33){var _34=this._getScrollBounds();this._leftBtn.set("disabled",_33<=_34.min);this._rightBtn.set("disabled",_33>=_34.max);}});var _35=_2("dijit.layout._ScrollingTabControllerButtonMixin",null,{baseClass:"dijitTab tabStripButton",templateString:_b,tabIndex:"",isFocusable:function(){return false;}});_2("dijit.layout._ScrollingTabControllerButton",[_11,_35]);_2("dijit.layout._ScrollingTabControllerMenuButton",[_11,_12,_35],{containerId:"",tabIndex:"-1",isLoaded:function(){return false;},loadDropDown:function(_36){this.dropDown=new _f({id:this.containerId+"_menu",dir:this.dir,lang:this.lang,textDir:this.textDir});var _37=_9.byId(this.containerId);_1.forEach(_37.getChildren(),function(_38){var _39=new _10({id:_38.id+"_stcMi",label:_38.title,iconClass:_38.iconClass,dir:_38.dir,lang:_38.lang,textDir:_38.textDir,onClick:function(){_37.selectChild(_38);}});this.dropDown.addChild(_39);},this);_36();},closeDropDown:function(_3a){this.inherited(arguments);if(this.dropDown){this.dropDown.destroyRecursive();delete this.dropDown;}}});return _13;});
\ No newline at end of file diff --git a/lib/dijit/layout/ScrollingTabController.js.uncompressed.js b/lib/dijit/layout/ScrollingTabController.js.uncompressed.js new file mode 100644 index 000000000..6a37f2189 --- /dev/null +++ b/lib/dijit/layout/ScrollingTabController.js.uncompressed.js @@ -0,0 +1,514 @@ +require({cache:{ +'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">◀</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>", +'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>"}}); +define("dijit/layout/ScrollingTabController", [ + "dojo/_base/array", // array.forEach + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add domClass.contains + "dojo/dom-geometry", // domGeometry.contentBox + "dojo/dom-style", // domStyle.style + "dojo/_base/fx", // Animation + "dojo/_base/lang", // lang.hitch + "dojo/query", // query + "dojo/_base/sniff", // has("ie"), has("webkit"), has("quirks") + "../registry", // registry.byId() + "dojo/text!./templates/ScrollingTabController.html", + "dojo/text!./templates/_ScrollingTabControllerButton.html", + "./TabController", + "./utils", // marginBox2contextBox, layoutChildren + "../_WidgetsInTemplateMixin", + "../Menu", + "../MenuItem", + "../form/Button", + "../_HasDropDown", + "dojo/NodeList-dom" // NodeList.style +], function(array, declare, domClass, domGeometry, domStyle, fx, lang, query, has, + registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin, + Menu, MenuItem, Button, _HasDropDown){ + +/*===== +var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin; +var Menu = dijit.Menu; +var _HasDropDown = dijit._HasDropDown; +var TabController = dijit.layout.TabController; +=====*/ + + +// module: +// dijit/layout/ScrollingTabController +// summary: +// Set of tabs with left/right arrow keys and a menu to switch between tabs not +// all fitting on a single row. + + +var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], { + // summary: + // Set of tabs with left/right arrow keys and a menu to switch between tabs not + // all fitting on a single row. + // Works only for horizontal tabs (either above or below the content, not to the left + // or right). + // tags: + // private + + baseClass: "dijitTabController dijitScrollingTabController", + + templateString: tabControllerTemplate, + + // useMenu: [const] Boolean + // True if a menu should be used to select tabs when they are too + // wide to fit the TabContainer, false otherwise. + useMenu: true, + + // useSlider: [const] Boolean + // True if a slider should be used to select tabs when they are too + // wide to fit the TabContainer, false otherwise. + useSlider: true, + + // tabStripClass: [const] String + // The css class to apply to the tab strip, if it is visible. + tabStripClass: "", + + widgetsInTemplate: true, + + // _minScroll: Number + // The distance in pixels from the edge of the tab strip which, + // if a scroll animation is less than, forces the scroll to + // go all the way to the left/right. + _minScroll: 5, + + // Override default behavior mapping class to DOMNode + _setClassAttr: { node: "containerNode", type: "class" }, + + buildRendering: function(){ + this.inherited(arguments); + var n = this.domNode; + + this.scrollNode = this.tablistWrapper; + this._initButtons(); + + if(!this.tabStripClass){ + this.tabStripClass = "dijitTabContainer" + + this.tabPosition.charAt(0).toUpperCase() + + this.tabPosition.substr(1).replace(/-.*/, "") + + "None"; + domClass.add(n, "tabStrip-disabled") + } + + domClass.add(this.tablistWrapper, this.tabStripClass); + }, + + onStartup: function(){ + this.inherited(arguments); + + // TabController is hidden until it finishes drawing, to give + // a less visually jumpy instantiation. When it's finished, set visibility to "" + // to that the tabs are hidden/shown depending on the container's visibility setting. + domStyle.set(this.domNode, "visibility", ""); + this._postStartup = true; + }, + + onAddChild: function(page, insertIndex){ + this.inherited(arguments); + + // changes to the tab button label or iconClass will have changed the width of the + // buttons, so do a resize + array.forEach(["label", "iconClass"], function(attr){ + this.pane2watches[page.id].push( + this.pane2button[page.id].watch(attr, lang.hitch(this, function(){ + if(this._postStartup && this._dim){ + this.resize(this._dim); + } + })) + ); + }, this); + + // Increment the width of the wrapper when a tab is added + // This makes sure that the buttons never wrap. + // The value 200 is chosen as it should be bigger than most + // Tab button widths. + domStyle.set(this.containerNode, "width", + (domStyle.get(this.containerNode, "width") + 200) + "px"); + }, + + onRemoveChild: function(page, insertIndex){ + // null out _selectedTab because we are about to delete that dom node + var button = this.pane2button[page.id]; + if(this._selectedTab === button.domNode){ + this._selectedTab = null; + } + + this.inherited(arguments); + }, + + _initButtons: function(){ + // summary: + // Creates the buttons used to scroll to view tabs that + // may not be visible if the TabContainer is too narrow. + + // Make a list of the buttons to display when the tab labels become + // wider than the TabContainer, and hide the other buttons. + // Also gets the total width of the displayed buttons. + this._btnWidth = 0; + this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){ + if((this.useMenu && btn == this._menuBtn.domNode) || + (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){ + this._btnWidth += domGeometry.getMarginSize(btn).w; + return true; + }else{ + domStyle.set(btn, "display", "none"); + return false; + } + }, this); + }, + + _getTabsWidth: function(){ + var children = this.getChildren(); + if(children.length){ + var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode, + rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode; + return rightTab.offsetLeft + domStyle.get(rightTab, "width") - leftTab.offsetLeft; + }else{ + return 0; + } + }, + + _enableBtn: function(width){ + // summary: + // Determines if the tabs are wider than the width of the TabContainer, and + // thus that we need to display left/right/menu navigation buttons. + var tabsWidth = this._getTabsWidth(); + width = width || domStyle.get(this.scrollNode, "width"); + return tabsWidth > 0 && width < tabsWidth; + }, + + resize: function(dim){ + // summary: + // Hides or displays the buttons used to scroll the tab list and launch the menu + // that selects tabs. + + // Save the dimensions to be used when a child is renamed. + this._dim = dim; + + // Set my height to be my natural height (tall enough for one row of tab labels), + // and my content-box width based on margin-box width specified in dim parameter. + // But first reset scrollNode.height in case it was set by layoutChildren() call + // in a previous run of this method. + this.scrollNode.style.height = "auto"; + var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w}); + cb.h = this.scrollNode.offsetHeight; + domGeometry.setContentSize(this.domNode, cb); + + // Show/hide the left/right/menu navigation buttons depending on whether or not they + // are needed. + var enable = this._enableBtn(this._contentBox.w); + this._buttons.style("display", enable ? "" : "none"); + + // Position and size the navigation buttons and the tablist + this._leftBtn.layoutAlign = "left"; + this._rightBtn.layoutAlign = "right"; + this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left"; + layoutUtils.layoutChildren(this.domNode, this._contentBox, + [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]); + + // set proper scroll so that selected tab is visible + if(this._selectedTab){ + if(this._anim && this._anim.status() == "playing"){ + this._anim.stop(); + } + this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab()); + } + + // Enable/disabled left right buttons depending on whether or not user can scroll to left or right + this._setButtonClass(this._getScroll()); + + this._postResize = true; + + // Return my size so layoutChildren() can use it. + // Also avoids IE9 layout glitch on browser resize when scroll buttons present + return {h: this._contentBox.h, w: dim.w}; + }, + + _getScroll: function(){ + // summary: + // Returns the current scroll of the tabs where 0 means + // "scrolled all the way to the left" and some positive number, based on # + // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right" + return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft : + domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width") + + (has("ie") == 8 ? -1 : 1) * this.scrollNode.scrollLeft; + }, + + _convertToScrollLeft: function(val){ + // summary: + // Given a scroll value where 0 means "scrolled all the way to the left" + // and some positive number, based on # of pixels of possible scroll (ex: 1000) + // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft + // to achieve that scroll. + // + // This method is to adjust for RTL funniness in various browsers and versions. + if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){ + return val; + }else{ + var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width"); + return (has("ie") == 8 ? -1 : 1) * (val - maxScroll); + } + }, + + onSelectChild: function(/*dijit._Widget*/ page){ + // summary: + // Smoothly scrolls to a tab when it is selected. + + var tab = this.pane2button[page.id]; + if(!tab || !page){return;} + + var node = tab.domNode; + + // Save the selection + if(node != this._selectedTab){ + this._selectedTab = node; + + // Scroll to the selected tab, except on startup, when scrolling is handled in resize() + if(this._postResize){ + var sl = this._getScroll(); + + if(sl > node.offsetLeft || + sl + domStyle.get(this.scrollNode, "width") < + node.offsetLeft + domStyle.get(node, "width")){ + this.createSmoothScroll().play(); + } + } + } + + this.inherited(arguments); + }, + + _getScrollBounds: function(){ + // summary: + // Returns the minimum and maximum scroll setting to show the leftmost and rightmost + // tabs (respectively) + var children = this.getChildren(), + scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px + containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px + maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible + tabsWidth = this._getTabsWidth(); + + if(children.length && tabsWidth > scrollNodeWidth){ + // Scrolling should happen + return { + min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft, + max: this.isLeftToRight() ? + (children[children.length-1].domNode.offsetLeft + domStyle.get(children[children.length-1].domNode, "width")) - scrollNodeWidth : + maxPossibleScroll + }; + }else{ + // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir) + var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll; + return { + min: onlyScrollPosition, + max: onlyScrollPosition + }; + } + }, + + _getScrollForSelectedTab: function(){ + // summary: + // Returns the scroll value setting so that the selected tab + // will appear in the center + var w = this.scrollNode, + n = this._selectedTab, + scrollNodeWidth = domStyle.get(this.scrollNode, "width"), + scrollBounds = this._getScrollBounds(); + + // TODO: scroll minimal amount (to either right or left) so that + // selected tab is fully visible, and just return if it's already visible? + var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2; + pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max); + + // TODO: + // If scrolling close to the left side or right side, scroll + // all the way to the left or right. See this._minScroll. + // (But need to make sure that doesn't scroll the tab out of view...) + return pos; + }, + + createSmoothScroll: function(x){ + // summary: + // Creates a dojo._Animation object that smoothly scrolls the tab list + // either to a fixed horizontal pixel value, or to the selected tab. + // description: + // If an number argument is passed to the function, that horizontal + // pixel position is scrolled to. Otherwise the currently selected + // tab is scrolled to. + // x: Integer? + // An optional pixel value to scroll to, indicating distance from left. + + // Calculate position to scroll to + if(arguments.length > 0){ + // position specified by caller, just make sure it's within bounds + var scrollBounds = this._getScrollBounds(); + x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max); + }else{ + // scroll to center the current tab + x = this._getScrollForSelectedTab(); + } + + if(this._anim && this._anim.status() == "playing"){ + this._anim.stop(); + } + + var self = this, + w = this.scrollNode, + anim = new fx.Animation({ + beforeBegin: function(){ + if(this.curve){ delete this.curve; } + var oldS = w.scrollLeft, + newS = self._convertToScrollLeft(x); + anim.curve = new fx._Line(oldS, newS); + }, + onAnimate: function(val){ + w.scrollLeft = val; + } + }); + this._anim = anim; + + // Disable/enable left/right buttons according to new scroll position + this._setButtonClass(x); + + return anim; // dojo._Animation + }, + + _getBtnNode: function(/*Event*/ e){ + // summary: + // Gets a button DOM node from a mouse click event. + // e: + // The mouse click event. + var n = e.target; + while(n && !domClass.contains(n, "tabStripButton")){ + n = n.parentNode; + } + return n; + }, + + doSlideRight: function(/*Event*/ e){ + // summary: + // Scrolls the menu to the right. + // e: + // The mouse click event. + this.doSlide(1, this._getBtnNode(e)); + }, + + doSlideLeft: function(/*Event*/ e){ + // summary: + // Scrolls the menu to the left. + // e: + // The mouse click event. + this.doSlide(-1,this._getBtnNode(e)); + }, + + doSlide: function(/*Number*/ direction, /*DomNode*/ node){ + // summary: + // Scrolls the tab list to the left or right by 75% of the widget width. + // direction: + // If the direction is 1, the widget scrolls to the right, if it is + // -1, it scrolls to the left. + + if(node && domClass.contains(node, "dijitTabDisabled")){return;} + + var sWidth = domStyle.get(this.scrollNode, "width"); + var d = (sWidth * 0.75) * direction; + + var to = this._getScroll() + d; + + this._setButtonClass(to); + + this.createSmoothScroll(to).play(); + }, + + _setButtonClass: function(/*Number*/ scroll){ + // summary: + // Disables the left scroll button if the tabs are scrolled all the way to the left, + // or the right scroll button in the opposite case. + // scroll: Integer + // amount of horizontal scroll + + var scrollBounds = this._getScrollBounds(); + this._leftBtn.set("disabled", scroll <= scrollBounds.min); + this._rightBtn.set("disabled", scroll >= scrollBounds.max); + } +}); + + +var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, { + baseClass: "dijitTab tabStripButton", + + templateString: buttonTemplate, + + // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be + // able to tab to the left/right/menu buttons + tabIndex: "", + + // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it + // either (this override avoids focus() call in FormWidget.js) + isFocusable: function(){ return false; } +}); +/*===== +ScrollingTabControllerButtonMixin = dijit.layout._ScrollingTabControllerButtonMixin; +=====*/ + +// Class used in template +declare("dijit.layout._ScrollingTabControllerButton", + [Button, ScrollingTabControllerButtonMixin]); + +// Class used in template +declare( + "dijit.layout._ScrollingTabControllerMenuButton", + [Button, _HasDropDown, ScrollingTabControllerButtonMixin], +{ + // id of the TabContainer itself + containerId: "", + + // -1 so user can't tab into the button, but so that button can still be focused programatically. + // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash. + tabIndex: "-1", + + isLoaded: function(){ + // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed + return false; + }, + + loadDropDown: function(callback){ + this.dropDown = new Menu({ + id: this.containerId + "_menu", + dir: this.dir, + lang: this.lang, + textDir: this.textDir + }); + var container = registry.byId(this.containerId); + array.forEach(container.getChildren(), function(page){ + var menuItem = new MenuItem({ + id: page.id + "_stcMi", + label: page.title, + iconClass: page.iconClass, + dir: page.dir, + lang: page.lang, + textDir: page.textDir, + onClick: function(){ + container.selectChild(page); + } + }); + this.dropDown.addChild(menuItem); + }, this); + callback(); + }, + + closeDropDown: function(/*Boolean*/ focus){ + this.inherited(arguments); + if(this.dropDown){ + this.dropDown.destroyRecursive(); + delete this.dropDown; + } + } +}); + +return ScrollingTabController; +}); diff --git a/lib/dijit/layout/SplitContainer.js b/lib/dijit/layout/SplitContainer.js index cb18273e4..165ba9e2e 100644 --- a/lib/dijit/layout/SplitContainer.js +++ b/lib/dijit/layout/SplitContainer.js @@ -1,589 +1,2 @@ -/* - 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.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.SplitContainer"] = true; -dojo.provide("dijit.layout.SplitContainer"); -dojo.require("dojo.cookie"); -dojo.require("dijit.layout._LayoutWidget"); - - -// -// FIXME: make it prettier -// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case) -// - - -dojo.declare("dijit.layout.SplitContainer", - dijit.layout._LayoutWidget, - { - // summary: - // Deprecated. Use `dijit.layout.BorderContainer` instead. - // description: - // A Container widget with sizing handles in-between each child. - // Contains multiple children widgets, all of which are displayed side by side - // (either horizontally or vertically); there's a bar between each of the children, - // and you can adjust the relative size of each child by dragging the bars. - // - // You must specify a size (width and height) for the SplitContainer. - // tags: - // deprecated - - constructor: function(){ - dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0); - }, - - // activeSizing: Boolean - // If true, the children's size changes as you drag the bar; - // otherwise, the sizes don't change until you drop the bar (by mouse-up) - activeSizing: false, - - // sizerWidth: Integer - // Size in pixels of the bar between each child - sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css) - - // orientation: String - // either 'horizontal' or vertical; indicates whether the children are - // arranged side-by-side or up/down. - orientation: 'horizontal', - - // persist: Boolean - // Save splitter positions in a cookie - persist: true, - - baseClass: "dijitSplitContainer", - - postMixInProperties: function(){ - this.inherited("postMixInProperties",arguments); - this.isHorizontal = (this.orientation == 'horizontal'); - }, - - postCreate: function(){ - this.inherited(arguments); - this.sizers = []; - - // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435) - // to keep other combined css classes from inadvertantly making the overflow visible - if(dojo.isMozilla){ - this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work - } - - // create the fake dragger - if(typeof this.sizerWidth == "object"){ - try{ //FIXME: do this without a try/catch - this.sizerWidth = parseInt(this.sizerWidth.toString()); - }catch(e){ this.sizerWidth = 7; } - } - var sizer = dojo.doc.createElement('div'); - this.virtualSizer = sizer; - sizer.style.position = 'relative'; - - // #1681: work around the dreaded 'quirky percentages in IE' layout bug - // If the splitcontainer's dimensions are specified in percentages, it - // will be resized when the virtualsizer is displayed in _showSizingLine - // (typically expanding its bounds unnecessarily). This happens because - // we use position: relative for .dijitSplitContainer. - // The workaround: instead of changing the display style attribute, - // switch to changing the zIndex (bring to front/move to back) - - sizer.style.zIndex = 10; - sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV'; - this.domNode.appendChild(sizer); - dojo.setSelectable(sizer, false); - }, - - destroy: function(){ - delete this.virtualSizer; - dojo.forEach(this._ownconnects, dojo.disconnect); - this.inherited(arguments); - }, - startup: function(){ - if(this._started){ return; } - - dojo.forEach(this.getChildren(), function(child, i, children){ - // attach the children and create the draggers - this._setupChild(child); - - if(i < children.length-1){ - this._addSizer(); - } - }, this); - - if(this.persist){ - this._restoreState(); - } - - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - this.inherited(arguments); - child.domNode.style.position = "absolute"; - dojo.addClass(child.domNode, "dijitSplitPane"); - }, - - _onSizerMouseDown: function(e){ - if(e.target.id){ - for(var i=0;i<this.sizers.length;i++){ - if(this.sizers[i].id == e.target.id){ - break; - } - } - if(i<this.sizers.length){ - this.beginSizing(e,i); - } - } - }, - _addSizer: function(index){ - index = index === undefined ? this.sizers.length : index; - - // TODO: use a template for this!!! - var sizer = dojo.doc.createElement('div'); - sizer.id=dijit.getUniqueId('dijit_layout_SplitterContainer_Splitter'); - this.sizers.splice(index,0,sizer); - this.domNode.appendChild(sizer); - - sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV'; - - // add the thumb div - var thumb = dojo.doc.createElement('div'); - thumb.className = 'thumb'; - sizer.appendChild(thumb); - - // FIXME: are you serious? why aren't we using mover start/stop combo? - this.connect(sizer, "onmousedown", '_onSizerMouseDown'); - - dojo.setSelectable(sizer, false); - }, - - removeChild: function(widget){ - // summary: - // Remove sizer, but only if widget is really our child and - // we have at least one sizer to throw away - if(this.sizers.length){ - var i=dojo.indexOf(this.getChildren(), widget) - if(i != -1){ - if(i == this.sizers.length){ - i--; - } - dojo.destroy(this.sizers[i]); - this.sizers.splice(i,1); - } - } - - // Remove widget and repaint - this.inherited(arguments); - if(this._started){ - this.layout(); - } - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // summary: - // Add a child widget to the container - // child: - // a widget to add - // insertIndex: - // postion in the "stack" to add the child widget - - this.inherited(arguments); - - if(this._started){ - // Do the stuff that startup() does for each widget - var children = this.getChildren(); - if(children.length > 1){ - this._addSizer(insertIndex); - } - - // and then reposition (ie, shrink) every pane to make room for the new guy - this.layout(); - } - }, - - layout: function(){ - // summary: - // Do layout of panels - - // base class defines this._contentBox on initial creation and also - // on resize - this.paneWidth = this._contentBox.w; - this.paneHeight = this._contentBox.h; - - var children = this.getChildren(); - if(!children.length){ return; } - - // - // calculate space - // - - var space = this.isHorizontal ? this.paneWidth : this.paneHeight; - if(children.length > 1){ - space -= this.sizerWidth * (children.length - 1); - } - - // - // calculate total of SizeShare values - // - var outOf = 0; - dojo.forEach(children, function(child){ - outOf += child.sizeShare; - }); - - // - // work out actual pixels per sizeshare unit - // - var pixPerUnit = space / outOf; - - // - // set the SizeActual member of each pane - // - var totalSize = 0; - dojo.forEach(children.slice(0, children.length - 1), function(child){ - var size = Math.round(pixPerUnit * child.sizeShare); - child.sizeActual = size; - totalSize += size; - }); - - children[children.length-1].sizeActual = space - totalSize; - - // - // make sure the sizes are ok - // - this._checkSizes(); - - // - // now loop, positioning each pane and letting children resize themselves - // - - var pos = 0; - var size = children[0].sizeActual; - this._movePanel(children[0], pos, size); - children[0].position = pos; - pos += size; - - // if we don't have any sizers, our layout method hasn't been called yet - // so bail until we are called..TODO: REVISIT: need to change the startup - // algorithm to guaranteed the ordering of calls to layout method - if(!this.sizers){ - return; - } - - dojo.some(children.slice(1), function(child, i){ - // error-checking - if(!this.sizers[i]){ - return true; - } - // first we position the sizing handle before this pane - this._moveSlider(this.sizers[i], pos, this.sizerWidth); - this.sizers[i].position = pos; - pos += this.sizerWidth; - - size = child.sizeActual; - this._movePanel(child, pos, size); - child.position = pos; - pos += size; - }, this); - }, - - _movePanel: function(panel, pos, size){ - if(this.isHorizontal){ - panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually - panel.domNode.style.top = 0; - var box = {w: size, h: this.paneHeight}; - if(panel.resize){ - panel.resize(box); - }else{ - dojo.marginBox(panel.domNode, box); - } - }else{ - panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually - panel.domNode.style.top = pos + 'px'; - var box = {w: this.paneWidth, h: size}; - if(panel.resize){ - panel.resize(box); - }else{ - dojo.marginBox(panel.domNode, box); - } - } - }, - - _moveSlider: function(slider, pos, size){ - if(this.isHorizontal){ - slider.style.left = pos + 'px'; - slider.style.top = 0; - dojo.marginBox(slider, { w: size, h: this.paneHeight }); - }else{ - slider.style.left = 0; - slider.style.top = pos + 'px'; - dojo.marginBox(slider, { w: this.paneWidth, h: size }); - } - }, - - _growPane: function(growth, pane){ - if(growth > 0){ - if(pane.sizeActual > pane.sizeMin){ - if((pane.sizeActual - pane.sizeMin) > growth){ - - // stick all the growth in this pane - pane.sizeActual = pane.sizeActual - growth; - growth = 0; - }else{ - // put as much growth in here as we can - growth -= pane.sizeActual - pane.sizeMin; - pane.sizeActual = pane.sizeMin; - } - } - } - return growth; - }, - - _checkSizes: function(){ - - var totalMinSize = 0; - var totalSize = 0; - var children = this.getChildren(); - - dojo.forEach(children, function(child){ - totalSize += child.sizeActual; - totalMinSize += child.sizeMin; - }); - - // only make adjustments if we have enough space for all the minimums - - if(totalMinSize <= totalSize){ - - var growth = 0; - - dojo.forEach(children, function(child){ - if(child.sizeActual < child.sizeMin){ - growth += child.sizeMin - child.sizeActual; - child.sizeActual = child.sizeMin; - } - }); - - if(growth > 0){ - var list = this.isDraggingLeft ? children.reverse() : children; - dojo.forEach(list, function(child){ - growth = this._growPane(growth, child); - }, this); - } - }else{ - dojo.forEach(children, function(child){ - child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize)); - }); - } - }, - - beginSizing: function(e, i){ - var children = this.getChildren(); - this.paneBefore = children[i]; - this.paneAfter = children[i+1]; - - this.isSizing = true; - this.sizingSplitter = this.sizers[i]; - - if(!this.cover){ - this.cover = dojo.create('div', { - style: { - position:'absolute', - zIndex:5, - top: 0, - left: 0, - width: "100%", - height: "100%" - } - }, this.domNode); - }else{ - this.cover.style.zIndex = 5; - } - this.sizingSplitter.style.zIndex = 6; - - // TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.) - this.originPos = dojo.position(children[0].domNode, true); - if(this.isHorizontal){ - var client = e.layerX || e.offsetX || 0; - var screen = e.pageX; - this.originPos = this.originPos.x; - }else{ - var client = e.layerY || e.offsetY || 0; - var screen = e.pageY; - this.originPos = this.originPos.y; - } - this.startPoint = this.lastPoint = screen; - this.screenToClientOffset = screen - client; - this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position; - - if(!this.activeSizing){ - this._showSizingLine(); - } - - // - // attach mouse events - // - this._ownconnects = []; - this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing")); - this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing")); - - dojo.stopEvent(e); - }, - - changeSizing: function(e){ - if(!this.isSizing){ return; } - this.lastPoint = this.isHorizontal ? e.pageX : e.pageY; - this.movePoint(); - if(this.activeSizing){ - this._updateSize(); - }else{ - this._moveSizingLine(); - } - dojo.stopEvent(e); - }, - - endSizing: function(e){ - if(!this.isSizing){ return; } - if(this.cover){ - this.cover.style.zIndex = -1; - } - if(!this.activeSizing){ - this._hideSizingLine(); - } - - this._updateSize(); - - this.isSizing = false; - - if(this.persist){ - this._saveState(this); - } - - dojo.forEach(this._ownconnects, dojo.disconnect); - }, - - movePoint: function(){ - - // make sure lastPoint is a legal point to drag to - var p = this.lastPoint - this.screenToClientOffset; - - var a = p - this.dragOffset; - a = this.legaliseSplitPoint(a); - p = a + this.dragOffset; - - this.lastPoint = p + this.screenToClientOffset; - }, - - legaliseSplitPoint: function(a){ - - a += this.sizingSplitter.position; - - this.isDraggingLeft = !!(a > 0); - - if(!this.activeSizing){ - var min = this.paneBefore.position + this.paneBefore.sizeMin; - if(a < min){ - a = min; - } - - var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin)); - if(a > max){ - a = max; - } - } - - a -= this.sizingSplitter.position; - - this._checkSizes(); - - return a; - }, - - _updateSize: function(){ - //FIXME: sometimes this.lastPoint is NaN - var pos = this.lastPoint - this.dragOffset - this.originPos; - - var start_region = this.paneBefore.position; - var end_region = this.paneAfter.position + this.paneAfter.sizeActual; - - this.paneBefore.sizeActual = pos - start_region; - this.paneAfter.position = pos + this.sizerWidth; - this.paneAfter.sizeActual = end_region - this.paneAfter.position; - - dojo.forEach(this.getChildren(), function(child){ - child.sizeShare = child.sizeActual; - }); - - if(this._started){ - this.layout(); - } - }, - - _showSizingLine: function(){ - - this._moveSizingLine(); - - dojo.marginBox(this.virtualSizer, - this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth }); - - this.virtualSizer.style.display = 'block'; - }, - - _hideSizingLine: function(){ - this.virtualSizer.style.display = 'none'; - }, - - _moveSizingLine: function(){ - var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position; - dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px"); - // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better - }, - - _getCookieName: function(i){ - return this.id + "_" + i; - }, - - _restoreState: function(){ - dojo.forEach(this.getChildren(), function(child, i){ - var cookieName = this._getCookieName(i); - var cookieValue = dojo.cookie(cookieName); - if(cookieValue){ - var pos = parseInt(cookieValue); - if(typeof pos == "number"){ - child.sizeShare = pos; - } - } - }, this); - }, - - _saveState: function(){ - if(!this.persist){ - return; - } - dojo.forEach(this.getChildren(), function(child, i){ - dojo.cookie(this._getCookieName(i), child.sizeShare, {expires:365}); - }, this); - } -}); - -// These arguments can be specified for the children of a SplitContainer. -// Since any widget can be specified as a SplitContainer child, mix them -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // sizeMin: [deprecated] Integer - // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. - // Minimum size (width or height) of a child of a SplitContainer. - // The value is relative to other children's sizeShare properties. - sizeMin: 10, - - // sizeShare: [deprecated] Integer - // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. - // Size (width or height) of a child of a SplitContainer. - // The value is relative to other children's sizeShare properties. - // For example, if there are two children and each has sizeShare=10, then - // each takes up 50% of the available space. - sizeShare: 10 -}); - -} +//>>built +define("dijit/layout/SplitContainer",["dojo/_base/array","dojo/cookie","dojo/_base/declare","dojo/dom","dojo/dom-class","dojo/dom-construct","dojo/dom-geometry","dojo/dom-style","dojo/_base/event","dojo/_base/kernel","dojo/_base/lang","dojo/on","dojo/_base/sniff","dojo/_base/window","../registry","../_WidgetBase","./_LayoutWidget"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,on,_c,_d,_e,_f,_10){_b.extend(_f,{sizeMin:10,sizeShare:10});return _3("dijit.layout.SplitContainer",_10,{constructor:function(){_a.deprecated("dijit.layout.SplitContainer is deprecated","use BorderContainer with splitter instead",2);},activeSizing:false,sizerWidth:7,orientation:"horizontal",persist:true,baseClass:"dijitSplitContainer",postMixInProperties:function(){this.inherited("postMixInProperties",arguments);this.isHorizontal=(this.orientation=="horizontal");},postCreate:function(){this.inherited(arguments);this.sizers=[];if(_c("mozilla")){this.domNode.style.overflow="-moz-scrollbars-none";}if(typeof this.sizerWidth=="object"){try{this.sizerWidth=parseInt(this.sizerWidth.toString());}catch(e){this.sizerWidth=7;}}var _11=_d.doc.createElement("div");this.virtualSizer=_11;_11.style.position="relative";_11.style.zIndex=10;_11.className=this.isHorizontal?"dijitSplitContainerVirtualSizerH":"dijitSplitContainerVirtualSizerV";this.domNode.appendChild(_11);_4.setSelectable(_11,false);},destroy:function(){delete this.virtualSizer;if(this._ownconnects){var h;while(h=this._ownconnects.pop()){h.remove();}}this.inherited(arguments);},startup:function(){if(this._started){return;}_1.forEach(this.getChildren(),function(_12,i,_13){this._setupChild(_12);if(i<_13.length-1){this._addSizer();}},this);if(this.persist){this._restoreState();}this.inherited(arguments);},_setupChild:function(_14){this.inherited(arguments);_14.domNode.style.position="absolute";_5.add(_14.domNode,"dijitSplitPane");},_onSizerMouseDown:function(e){if(e.target.id){for(var i=0;i<this.sizers.length;i++){if(this.sizers[i].id==e.target.id){break;}}if(i<this.sizers.length){this.beginSizing(e,i);}}},_addSizer:function(_15){_15=_15===undefined?this.sizers.length:_15;var _16=_d.doc.createElement("div");_16.id=_e.getUniqueId("dijit_layout_SplitterContainer_Splitter");this.sizers.splice(_15,0,_16);this.domNode.appendChild(_16);_16.className=this.isHorizontal?"dijitSplitContainerSizerH":"dijitSplitContainerSizerV";var _17=_d.doc.createElement("div");_17.className="thumb";_16.appendChild(_17);this.connect(_16,"onmousedown","_onSizerMouseDown");_4.setSelectable(_16,false);},removeChild:function(_18){if(this.sizers.length){var i=_1.indexOf(this.getChildren(),_18);if(i!=-1){if(i==this.sizers.length){i--;}_6.destroy(this.sizers[i]);this.sizers.splice(i,1);}}this.inherited(arguments);if(this._started){this.layout();}},addChild:function(_19,_1a){this.inherited(arguments);if(this._started){var _1b=this.getChildren();if(_1b.length>1){this._addSizer(_1a);}this.layout();}},layout:function(){this.paneWidth=this._contentBox.w;this.paneHeight=this._contentBox.h;var _1c=this.getChildren();if(!_1c.length){return;}var _1d=this.isHorizontal?this.paneWidth:this.paneHeight;if(_1c.length>1){_1d-=this.sizerWidth*(_1c.length-1);}var _1e=0;_1.forEach(_1c,function(_1f){_1e+=_1f.sizeShare;});var _20=_1d/_1e;var _21=0;_1.forEach(_1c.slice(0,_1c.length-1),function(_22){var _23=Math.round(_20*_22.sizeShare);_22.sizeActual=_23;_21+=_23;});_1c[_1c.length-1].sizeActual=_1d-_21;this._checkSizes();var pos=0;var _24=_1c[0].sizeActual;this._movePanel(_1c[0],pos,_24);_1c[0].position=pos;pos+=_24;if(!this.sizers){return;}_1.some(_1c.slice(1),function(_25,i){if(!this.sizers[i]){return true;}this._moveSlider(this.sizers[i],pos,this.sizerWidth);this.sizers[i].position=pos;pos+=this.sizerWidth;_24=_25.sizeActual;this._movePanel(_25,pos,_24);_25.position=pos;pos+=_24;},this);},_movePanel:function(_26,pos,_27){var box;if(this.isHorizontal){_26.domNode.style.left=pos+"px";_26.domNode.style.top=0;box={w:_27,h:this.paneHeight};if(_26.resize){_26.resize(box);}else{_7.setMarginBox(_26.domNode,box);}}else{_26.domNode.style.left=0;_26.domNode.style.top=pos+"px";box={w:this.paneWidth,h:_27};if(_26.resize){_26.resize(box);}else{_7.setMarginBox(_26.domNode,box);}}},_moveSlider:function(_28,pos,_29){if(this.isHorizontal){_28.style.left=pos+"px";_28.style.top=0;_7.setMarginBox(_28,{w:_29,h:this.paneHeight});}else{_28.style.left=0;_28.style.top=pos+"px";_7.setMarginBox(_28,{w:this.paneWidth,h:_29});}},_growPane:function(_2a,_2b){if(_2a>0){if(_2b.sizeActual>_2b.sizeMin){if((_2b.sizeActual-_2b.sizeMin)>_2a){_2b.sizeActual=_2b.sizeActual-_2a;_2a=0;}else{_2a-=_2b.sizeActual-_2b.sizeMin;_2b.sizeActual=_2b.sizeMin;}}}return _2a;},_checkSizes:function(){var _2c=0;var _2d=0;var _2e=this.getChildren();_1.forEach(_2e,function(_2f){_2d+=_2f.sizeActual;_2c+=_2f.sizeMin;});if(_2c<=_2d){var _30=0;_1.forEach(_2e,function(_31){if(_31.sizeActual<_31.sizeMin){_30+=_31.sizeMin-_31.sizeActual;_31.sizeActual=_31.sizeMin;}});if(_30>0){var _32=this.isDraggingLeft?_2e.reverse():_2e;_1.forEach(_32,function(_33){_30=this._growPane(_30,_33);},this);}}else{_1.forEach(_2e,function(_34){_34.sizeActual=Math.round(_2d*(_34.sizeMin/_2c));});}},beginSizing:function(e,i){var _35=this.getChildren();this.paneBefore=_35[i];this.paneAfter=_35[i+1];this.isSizing=true;this.sizingSplitter=this.sizers[i];if(!this.cover){this.cover=_6.create("div",{style:{position:"absolute",zIndex:5,top:0,left:0,width:"100%",height:"100%"}},this.domNode);}else{this.cover.style.zIndex=5;}this.sizingSplitter.style.zIndex=6;this.originPos=_7.position(_35[0].domNode,true);var _36,_37;if(this.isHorizontal){_36=e.layerX||e.offsetX||0;_37=e.pageX;this.originPos=this.originPos.x;}else{_36=e.layerY||e.offsetY||0;_37=e.pageY;this.originPos=this.originPos.y;}this.startPoint=this.lastPoint=_37;this.screenToClientOffset=_37-_36;this.dragOffset=this.lastPoint-this.paneBefore.sizeActual-this.originPos-this.paneBefore.position;if(!this.activeSizing){this._showSizingLine();}this._ownconnects=[on(_d.doc.documentElement,"mousemove",_b.hitch(this,"changeSizing")),on(_d.doc.documentElement,"mouseup",_b.hitch(this,"endSizing"))];_9.stop(e);},changeSizing:function(e){if(!this.isSizing){return;}this.lastPoint=this.isHorizontal?e.pageX:e.pageY;this.movePoint();if(this.activeSizing){this._updateSize();}else{this._moveSizingLine();}_9.stop(e);},endSizing:function(){if(!this.isSizing){return;}if(this.cover){this.cover.style.zIndex=-1;}if(!this.activeSizing){this._hideSizingLine();}this._updateSize();this.isSizing=false;if(this.persist){this._saveState(this);}var h;while(h=this._ownconnects.pop()){h.remove();}},movePoint:function(){var p=this.lastPoint-this.screenToClientOffset;var a=p-this.dragOffset;a=this.legaliseSplitPoint(a);p=a+this.dragOffset;this.lastPoint=p+this.screenToClientOffset;},legaliseSplitPoint:function(a){a+=this.sizingSplitter.position;this.isDraggingLeft=!!(a>0);if(!this.activeSizing){var min=this.paneBefore.position+this.paneBefore.sizeMin;if(a<min){a=min;}var max=this.paneAfter.position+(this.paneAfter.sizeActual-(this.sizerWidth+this.paneAfter.sizeMin));if(a>max){a=max;}}a-=this.sizingSplitter.position;this._checkSizes();return a;},_updateSize:function(){var pos=this.lastPoint-this.dragOffset-this.originPos;var _38=this.paneBefore.position;var _39=this.paneAfter.position+this.paneAfter.sizeActual;this.paneBefore.sizeActual=pos-_38;this.paneAfter.position=pos+this.sizerWidth;this.paneAfter.sizeActual=_39-this.paneAfter.position;_1.forEach(this.getChildren(),function(_3a){_3a.sizeShare=_3a.sizeActual;});if(this._started){this.layout();}},_showSizingLine:function(){this._moveSizingLine();_7.setMarginBox(this.virtualSizer,this.isHorizontal?{w:this.sizerWidth,h:this.paneHeight}:{w:this.paneWidth,h:this.sizerWidth});this.virtualSizer.style.display="block";},_hideSizingLine:function(){this.virtualSizer.style.display="none";},_moveSizingLine:function(){var pos=(this.lastPoint-this.startPoint)+this.sizingSplitter.position;_8.set(this.virtualSizer,(this.isHorizontal?"left":"top"),pos+"px");},_getCookieName:function(i){return this.id+"_"+i;},_restoreState:function(){_1.forEach(this.getChildren(),function(_3b,i){var _3c=this._getCookieName(i);var _3d=_2(_3c);if(_3d){var pos=parseInt(_3d);if(typeof pos=="number"){_3b.sizeShare=pos;}}},this);},_saveState:function(){if(!this.persist){return;}_1.forEach(this.getChildren(),function(_3e,i){_2(this._getCookieName(i),_3e.sizeShare,{expires:365});},this);}});});
\ No newline at end of file diff --git a/lib/dijit/layout/SplitContainer.js.uncompressed.js b/lib/dijit/layout/SplitContainer.js.uncompressed.js new file mode 100644 index 000000000..5b411c931 --- /dev/null +++ b/lib/dijit/layout/SplitContainer.js.uncompressed.js @@ -0,0 +1,611 @@ +define("dijit/layout/SplitContainer", [ + "dojo/_base/array", // array.forEach array.indexOf array.some + "dojo/cookie", // cookie + "dojo/_base/declare", // declare + "dojo/dom", // dom.setSelectable + "dojo/dom-class", // domClass.add + "dojo/dom-construct", // domConstruct.create domConstruct.destroy + "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position + "dojo/dom-style", // domStyle.style + "dojo/_base/event", // event.stop + "dojo/_base/kernel", // kernel.deprecated + "dojo/_base/lang", // lang.extend lang.hitch + "dojo/on", + "dojo/_base/sniff", // has("mozilla") + "dojo/_base/window", // win.doc.createElement win.doc.documentElement + "../registry", // registry.getUniqueId() + "../_WidgetBase", + "./_LayoutWidget" +], function(array, cookie, declare, dom, domClass, domConstruct, domGeometry, domStyle, + event, kernel, lang, on, has, win, registry, _WidgetBase, _LayoutWidget){ + +/*===== +var _WidgetBase = dijit._WidgetBase; +var _LayoutWidget = dijit.layout._LayoutWidget; +=====*/ + +// module: +// dijit/layout/SplitContainer +// summary: +// Deprecated. Use `dijit.layout.BorderContainer` instead. + +// +// FIXME: make it prettier +// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case) +// FIXME: sizeWidth should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css) +// + +// These arguments can be specified for the children of a SplitContainer. +// Since any widget can be specified as a SplitContainer child, mix them +// into the base widget class. (This is a hack, but it's effective.) +lang.extend(_WidgetBase, { + // sizeMin: [deprecated] Integer + // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. + // Minimum size (width or height) of a child of a SplitContainer. + // The value is relative to other children's sizeShare properties. + sizeMin: 10, + + // sizeShare: [deprecated] Integer + // Deprecated. Parameter for children of `dijit.layout.SplitContainer`. + // Size (width or height) of a child of a SplitContainer. + // The value is relative to other children's sizeShare properties. + // For example, if there are two children and each has sizeShare=10, then + // each takes up 50% of the available space. + sizeShare: 10 +}); + +return declare("dijit.layout.SplitContainer", _LayoutWidget, { + // summary: + // Deprecated. Use `dijit.layout.BorderContainer` instead. + // description: + // A Container widget with sizing handles in-between each child. + // Contains multiple children widgets, all of which are displayed side by side + // (either horizontally or vertically); there's a bar between each of the children, + // and you can adjust the relative size of each child by dragging the bars. + // + // You must specify a size (width and height) for the SplitContainer. + // tags: + // deprecated + + constructor: function(){ + kernel.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0); + }, + + // activeSizing: Boolean + // If true, the children's size changes as you drag the bar; + // otherwise, the sizes don't change until you drop the bar (by mouse-up) + activeSizing: false, + + // sizerWidth: Integer + // Size in pixels of the bar between each child + sizerWidth: 7, + + // orientation: String + // either 'horizontal' or vertical; indicates whether the children are + // arranged side-by-side or up/down. + orientation: 'horizontal', + + // persist: Boolean + // Save splitter positions in a cookie + persist: true, + + baseClass: "dijitSplitContainer", + + postMixInProperties: function(){ + this.inherited("postMixInProperties",arguments); + this.isHorizontal = (this.orientation == 'horizontal'); + }, + + postCreate: function(){ + this.inherited(arguments); + this.sizers = []; + + // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435) + // to keep other combined css classes from inadvertantly making the overflow visible + if(has("mozilla")){ + this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work + } + + // create the fake dragger + if(typeof this.sizerWidth == "object"){ + try{ //FIXME: do this without a try/catch + this.sizerWidth = parseInt(this.sizerWidth.toString()); + }catch(e){ this.sizerWidth = 7; } + } + var sizer = win.doc.createElement('div'); + this.virtualSizer = sizer; + sizer.style.position = 'relative'; + + // #1681: work around the dreaded 'quirky percentages in IE' layout bug + // If the splitcontainer's dimensions are specified in percentages, it + // will be resized when the virtualsizer is displayed in _showSizingLine + // (typically expanding its bounds unnecessarily). This happens because + // we use position: relative for .dijitSplitContainer. + // The workaround: instead of changing the display style attribute, + // switch to changing the zIndex (bring to front/move to back) + + sizer.style.zIndex = 10; + sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV'; + this.domNode.appendChild(sizer); + dom.setSelectable(sizer, false); + }, + + destroy: function(){ + delete this.virtualSizer; + if(this._ownconnects){ + var h; + while(h = this._ownconnects.pop()){ h.remove(); } + } + this.inherited(arguments); + }, + startup: function(){ + if(this._started){ return; } + + array.forEach(this.getChildren(), function(child, i, children){ + // attach the children and create the draggers + this._setupChild(child); + + if(i < children.length-1){ + this._addSizer(); + } + }, this); + + if(this.persist){ + this._restoreState(); + } + + this.inherited(arguments); + }, + + _setupChild: function(/*dijit._Widget*/ child){ + this.inherited(arguments); + child.domNode.style.position = "absolute"; + domClass.add(child.domNode, "dijitSplitPane"); + }, + + _onSizerMouseDown: function(e){ + if(e.target.id){ + for(var i=0;i<this.sizers.length;i++){ + if(this.sizers[i].id == e.target.id){ + break; + } + } + if(i<this.sizers.length){ + this.beginSizing(e,i); + } + } + }, + _addSizer: function(index){ + index = index === undefined ? this.sizers.length : index; + + // TODO: use a template for this!!! + var sizer = win.doc.createElement('div'); + sizer.id=registry.getUniqueId('dijit_layout_SplitterContainer_Splitter'); + this.sizers.splice(index,0,sizer); + this.domNode.appendChild(sizer); + + sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV'; + + // add the thumb div + var thumb = win.doc.createElement('div'); + thumb.className = 'thumb'; + sizer.appendChild(thumb); + + // FIXME: are you serious? why aren't we using mover start/stop combo? + this.connect(sizer, "onmousedown", '_onSizerMouseDown'); + + dom.setSelectable(sizer, false); + }, + + removeChild: function(widget){ + // summary: + // Remove sizer, but only if widget is really our child and + // we have at least one sizer to throw away + if(this.sizers.length){ + var i = array.indexOf(this.getChildren(), widget); + if(i != -1){ + if(i == this.sizers.length){ + i--; + } + domConstruct.destroy(this.sizers[i]); + this.sizers.splice(i,1); + } + } + + // Remove widget and repaint + this.inherited(arguments); + if(this._started){ + this.layout(); + } + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + // summary: + // Add a child widget to the container + // child: + // a widget to add + // insertIndex: + // postion in the "stack" to add the child widget + + this.inherited(arguments); + + if(this._started){ + // Do the stuff that startup() does for each widget + var children = this.getChildren(); + if(children.length > 1){ + this._addSizer(insertIndex); + } + + // and then reposition (ie, shrink) every pane to make room for the new guy + this.layout(); + } + }, + + layout: function(){ + // summary: + // Do layout of panels + + // base class defines this._contentBox on initial creation and also + // on resize + this.paneWidth = this._contentBox.w; + this.paneHeight = this._contentBox.h; + + var children = this.getChildren(); + if(!children.length){ return; } + + // + // calculate space + // + + var space = this.isHorizontal ? this.paneWidth : this.paneHeight; + if(children.length > 1){ + space -= this.sizerWidth * (children.length - 1); + } + + // + // calculate total of SizeShare values + // + var outOf = 0; + array.forEach(children, function(child){ + outOf += child.sizeShare; + }); + + // + // work out actual pixels per sizeshare unit + // + var pixPerUnit = space / outOf; + + // + // set the SizeActual member of each pane + // + var totalSize = 0; + array.forEach(children.slice(0, children.length - 1), function(child){ + var size = Math.round(pixPerUnit * child.sizeShare); + child.sizeActual = size; + totalSize += size; + }); + + children[children.length-1].sizeActual = space - totalSize; + + // + // make sure the sizes are ok + // + this._checkSizes(); + + // + // now loop, positioning each pane and letting children resize themselves + // + + var pos = 0; + var size = children[0].sizeActual; + this._movePanel(children[0], pos, size); + children[0].position = pos; + pos += size; + + // if we don't have any sizers, our layout method hasn't been called yet + // so bail until we are called..TODO: REVISIT: need to change the startup + // algorithm to guaranteed the ordering of calls to layout method + if(!this.sizers){ + return; + } + + array.some(children.slice(1), function(child, i){ + // error-checking + if(!this.sizers[i]){ + return true; + } + // first we position the sizing handle before this pane + this._moveSlider(this.sizers[i], pos, this.sizerWidth); + this.sizers[i].position = pos; + pos += this.sizerWidth; + + size = child.sizeActual; + this._movePanel(child, pos, size); + child.position = pos; + pos += size; + }, this); + }, + + _movePanel: function(panel, pos, size){ + var box; + if(this.isHorizontal){ + panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually + panel.domNode.style.top = 0; + box = {w: size, h: this.paneHeight}; + if(panel.resize){ + panel.resize(box); + }else{ + domGeometry.setMarginBox(panel.domNode, box); + } + }else{ + panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually + panel.domNode.style.top = pos + 'px'; + box = {w: this.paneWidth, h: size}; + if(panel.resize){ + panel.resize(box); + }else{ + domGeometry.setMarginBox(panel.domNode, box); + } + } + }, + + _moveSlider: function(slider, pos, size){ + if(this.isHorizontal){ + slider.style.left = pos + 'px'; + slider.style.top = 0; + domGeometry.setMarginBox(slider, { w: size, h: this.paneHeight }); + }else{ + slider.style.left = 0; + slider.style.top = pos + 'px'; + domGeometry.setMarginBox(slider, { w: this.paneWidth, h: size }); + } + }, + + _growPane: function(growth, pane){ + if(growth > 0){ + if(pane.sizeActual > pane.sizeMin){ + if((pane.sizeActual - pane.sizeMin) > growth){ + + // stick all the growth in this pane + pane.sizeActual = pane.sizeActual - growth; + growth = 0; + }else{ + // put as much growth in here as we can + growth -= pane.sizeActual - pane.sizeMin; + pane.sizeActual = pane.sizeMin; + } + } + } + return growth; + }, + + _checkSizes: function(){ + + var totalMinSize = 0; + var totalSize = 0; + var children = this.getChildren(); + + array.forEach(children, function(child){ + totalSize += child.sizeActual; + totalMinSize += child.sizeMin; + }); + + // only make adjustments if we have enough space for all the minimums + + if(totalMinSize <= totalSize){ + + var growth = 0; + + array.forEach(children, function(child){ + if(child.sizeActual < child.sizeMin){ + growth += child.sizeMin - child.sizeActual; + child.sizeActual = child.sizeMin; + } + }); + + if(growth > 0){ + var list = this.isDraggingLeft ? children.reverse() : children; + array.forEach(list, function(child){ + growth = this._growPane(growth, child); + }, this); + } + }else{ + array.forEach(children, function(child){ + child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize)); + }); + } + }, + + beginSizing: function(e, i){ + var children = this.getChildren(); + this.paneBefore = children[i]; + this.paneAfter = children[i+1]; + + this.isSizing = true; + this.sizingSplitter = this.sizers[i]; + + if(!this.cover){ + this.cover = domConstruct.create('div', { + style: { + position:'absolute', + zIndex:5, + top: 0, + left: 0, + width: "100%", + height: "100%" + } + }, this.domNode); + }else{ + this.cover.style.zIndex = 5; + } + this.sizingSplitter.style.zIndex = 6; + + // TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.) + this.originPos = domGeometry.position(children[0].domNode, true); + var client, screen; + if(this.isHorizontal){ + client = e.layerX || e.offsetX || 0; + screen = e.pageX; + this.originPos = this.originPos.x; + }else{ + client = e.layerY || e.offsetY || 0; + screen = e.pageY; + this.originPos = this.originPos.y; + } + this.startPoint = this.lastPoint = screen; + this.screenToClientOffset = screen - client; + this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position; + + if(!this.activeSizing){ + this._showSizingLine(); + } + + // + // attach mouse events + // + this._ownconnects = [ + on(win.doc.documentElement, "mousemove", lang.hitch(this, "changeSizing")), + on(win.doc.documentElement, "mouseup", lang.hitch(this, "endSizing")) + ]; + + event.stop(e); + }, + + changeSizing: function(e){ + if(!this.isSizing){ return; } + this.lastPoint = this.isHorizontal ? e.pageX : e.pageY; + this.movePoint(); + if(this.activeSizing){ + this._updateSize(); + }else{ + this._moveSizingLine(); + } + event.stop(e); + }, + + endSizing: function(){ + if(!this.isSizing){ return; } + if(this.cover){ + this.cover.style.zIndex = -1; + } + if(!this.activeSizing){ + this._hideSizingLine(); + } + + this._updateSize(); + + this.isSizing = false; + + if(this.persist){ + this._saveState(this); + } + + var h; + while(h = this._ownconnects.pop()){ h.remove(); } + }, + + movePoint: function(){ + + // make sure lastPoint is a legal point to drag to + var p = this.lastPoint - this.screenToClientOffset; + + var a = p - this.dragOffset; + a = this.legaliseSplitPoint(a); + p = a + this.dragOffset; + + this.lastPoint = p + this.screenToClientOffset; + }, + + legaliseSplitPoint: function(a){ + + a += this.sizingSplitter.position; + + this.isDraggingLeft = !!(a > 0); + + if(!this.activeSizing){ + var min = this.paneBefore.position + this.paneBefore.sizeMin; + if(a < min){ + a = min; + } + + var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin)); + if(a > max){ + a = max; + } + } + + a -= this.sizingSplitter.position; + + this._checkSizes(); + + return a; + }, + + _updateSize: function(){ + //FIXME: sometimes this.lastPoint is NaN + var pos = this.lastPoint - this.dragOffset - this.originPos; + + var start_region = this.paneBefore.position; + var end_region = this.paneAfter.position + this.paneAfter.sizeActual; + + this.paneBefore.sizeActual = pos - start_region; + this.paneAfter.position = pos + this.sizerWidth; + this.paneAfter.sizeActual = end_region - this.paneAfter.position; + + array.forEach(this.getChildren(), function(child){ + child.sizeShare = child.sizeActual; + }); + + if(this._started){ + this.layout(); + } + }, + + _showSizingLine: function(){ + + this._moveSizingLine(); + + domGeometry.setMarginBox(this.virtualSizer, + this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth }); + + this.virtualSizer.style.display = 'block'; + }, + + _hideSizingLine: function(){ + this.virtualSizer.style.display = 'none'; + }, + + _moveSizingLine: function(){ + var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position; + domStyle.set(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px"); + // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better + }, + + _getCookieName: function(i){ + return this.id + "_" + i; + }, + + _restoreState: function(){ + array.forEach(this.getChildren(), function(child, i){ + var cookieName = this._getCookieName(i); + var cookieValue = cookie(cookieName); + if(cookieValue){ + var pos = parseInt(cookieValue); + if(typeof pos == "number"){ + child.sizeShare = pos; + } + } + }, this); + }, + + _saveState: function(){ + if(!this.persist){ + return; + } + array.forEach(this.getChildren(), function(child, i){ + cookie(this._getCookieName(i), child.sizeShare, {expires:365}); + }, this); + } +}); + +}); diff --git a/lib/dijit/layout/StackContainer.js b/lib/dijit/layout/StackContainer.js index 98af4c942..b3e5ac15c 100644 --- a/lib/dijit/layout/StackContainer.js +++ b/lib/dijit/layout/StackContainer.js @@ -1,336 +1,2 @@ -/* - 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.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.StackContainer"] = true; -dojo.provide("dijit.layout.StackContainer"); -dojo.require("dijit._Templated"); -dojo.require("dijit.layout._LayoutWidget"); -dojo.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"); -dojo.require("dojo.cookie"); -dojo.require("dijit.layout.StackController"); - - -dojo.declare( - "dijit.layout.StackContainer", - dijit.layout._LayoutWidget, - { - // summary: - // A container that has multiple children, but shows only - // one child at a time - // - // description: - // A container for widgets (ContentPanes, for example) That displays - // only one Widget at a time. - // - // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild - // - // Can be base class for container, Wizard, Show, etc. - - // doLayout: Boolean - // If true, change the size of my currently displayed child to match my size - doLayout: true, - - // persist: Boolean - // Remembers the selected child across sessions - persist: false, - - baseClass: "dijitStackContainer", - -/*===== - // selectedChildWidget: [readonly] dijit._Widget - // References the currently selected child widget, if any. - // Adjust selected child with selectChild() method. - selectedChildWidget: null, -=====*/ - - buildRendering: function(){ - this.inherited(arguments); - dojo.addClass(this.domNode, "dijitLayoutContainer"); - dijit.setWaiRole(this.containerNode, "tabpanel"); - }, - - postCreate: function(){ - this.inherited(arguments); - this.connect(this.domNode, "onkeypress", this._onKeyPress); - }, - - startup: function(){ - if(this._started){ return; } - - var children = this.getChildren(); - - // Setup each page panel to be initially hidden - dojo.forEach(children, this._setupChild, this); - - // Figure out which child to initially display, defaulting to first one - if(this.persist){ - this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild")); - }else{ - dojo.some(children, function(child){ - if(child.selected){ - this.selectedChildWidget = child; - } - return child.selected; - }, this); - } - var selected = this.selectedChildWidget; - if(!selected && children[0]){ - selected = this.selectedChildWidget = children[0]; - selected.selected = true; - } - - // Publish information about myself so any StackControllers can initialize. - // This needs to happen before this.inherited(arguments) so that for - // TabContainer, this._contentBox doesn't include the space for the tab labels. - dojo.publish(this.id+"-startup", [{children: children, selected: selected}]); - - // Startup each child widget, and do initial layout like setting this._contentBox, - // then calls this.resize() which does the initial sizing on the selected child. - this.inherited(arguments); - }, - - resize: function(){ - // Resize is called when we are first made visible (it's called from startup() - // if we are initially visible). If this is the first time we've been made - // visible then show our first child. - var selected = this.selectedChildWidget; - if(selected && !this._hasBeenShown){ - this._hasBeenShown = true; - this._showChild(selected); - } - this.inherited(arguments); - }, - - _setupChild: function(/*dijit._Widget*/ child){ - // Overrides _LayoutWidget._setupChild() - - this.inherited(arguments); - - dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible"); - - // remove the title attribute so it doesn't show up when i hover - // over a node - child.domNode.title = ""; - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // Overrides _Container.addChild() to do layout and publish events - - this.inherited(arguments); - - if(this._started){ - dojo.publish(this.id+"-addChild", [child, insertIndex]); - - // in case the tab titles have overflowed from one line to two lines - // (or, if this if first child, from zero lines to one line) - // TODO: w/ScrollingTabController this is no longer necessary, although - // ScrollTabController.resize() does need to get called to show/hide - // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild() - this.layout(); - - // if this is the first child, then select it - if(!this.selectedChildWidget){ - this.selectChild(child); - } - } - }, - - removeChild: function(/*dijit._Widget*/ page){ - // Overrides _Container.removeChild() to do layout and publish events - - this.inherited(arguments); - - if(this._started){ - // this will notify any tablists to remove a button; do this first because it may affect sizing - dojo.publish(this.id + "-removeChild", [page]); - } - - // If we are being destroyed than don't run the code below (to select another page), because we are deleting - // every page one by one - if(this._beingDestroyed){ return; } - - // Select new page to display, also updating TabController to show the respective tab. - // Do this before layout call because it can affect the height of the TabController. - if(this.selectedChildWidget === page){ - this.selectedChildWidget = undefined; - if(this._started){ - var children = this.getChildren(); - if(children.length){ - this.selectChild(children[0]); - } - } - } - - if(this._started){ - // In case the tab titles now take up one line instead of two lines - // (note though that ScrollingTabController never overflows to multiple lines), - // or the height has changed slightly because of addition/removal of tab which close icon - this.layout(); - } - }, - - selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){ - // summary: - // Show the given widget (which must be one of my children) - // page: - // Reference to child widget or id of child widget - - page = dijit.byId(page); - - if(this.selectedChildWidget != page){ - // Deselect old page and select new one - var d = this._transition(page, this.selectedChildWidget, animate); - this._set("selectedChildWidget", page); - dojo.publish(this.id+"-selectChild", [page]); - - if(this.persist){ - dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id); - } - } - - return d; // If child has an href, promise that fires when the child's href finishes loading - }, - - _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){ - // summary: - // Hide the old widget and display the new widget. - // Subclasses should override this. - // tags: - // protected extension - if(oldWidget){ - this._hideChild(oldWidget); - } - var d = this._showChild(newWidget); - - // 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(newWidget.resize){ - if(this.doLayout){ - newWidget.resize(this._containerContentBox || this._contentBox); - }else{ - // the child should pick it's own size but we still need to call resize() - // (with no arguments) to let the widget lay itself out - newWidget.resize(); - } - } - - return d; // If child has an href, promise that fires when the child's href finishes loading - }, - - _adjacent: function(/*Boolean*/ forward){ - // summary: - // Gets the next/previous child widget in this container from the current selection. - var children = this.getChildren(); - var index = dojo.indexOf(children, this.selectedChildWidget); - index += forward ? 1 : children.length - 1; - return children[ index % children.length ]; // dijit._Widget - }, - - forward: function(){ - // summary: - // Advance to next page. - return this.selectChild(this._adjacent(true), true); - }, - - back: function(){ - // summary: - // Go back to previous page. - return this.selectChild(this._adjacent(false), true); - }, - - _onKeyPress: function(e){ - dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]); - }, - - layout: function(){ - // Implement _LayoutWidget.layout() virtual method. - if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){ - this.selectedChildWidget.resize(this._containerContentBox || this._contentBox); - } - }, - - _showChild: function(/*dijit._Widget*/ page){ - // summary: - // Show the specified child by changing it's CSS, and call _onShow()/onShow() so - // it can do any updates it needs regarding loading href's etc. - // returns: - // Promise that fires when page has finished showing, or true if there's no href - var children = this.getChildren(); - page.isFirstChild = (page == children[0]); - page.isLastChild = (page == children[children.length-1]); - page._set("selected", true); - - dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden"); - - return page._onShow() || true; - }, - - _hideChild: function(/*dijit._Widget*/ page){ - // summary: - // Hide the specified child by changing it's CSS, and call _onHide() so - // it's notified. - page._set("selected", false); - dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible"); - - page.onHide(); - }, - - closeChild: function(/*dijit._Widget*/ page){ - // summary: - // Callback when user clicks the [X] to remove a page. - // If onClose() returns true then remove and destroy the child. - // tags: - // private - var remove = page.onClose(this, page); - if(remove){ - this.removeChild(page); - // makes sure we can clean up executeScripts in ContentPane onUnLoad - page.destroyRecursive(); - } - }, - - destroyDescendants: function(/*Boolean*/ preserveDom){ - dojo.forEach(this.getChildren(), function(child){ - this.removeChild(child); - child.destroyRecursive(preserveDom); - }, this); - } -}); - -// For back-compat, remove for 2.0 - - -// These arguments can be specified for the children of a StackContainer. -// Since any widget can be specified as a StackContainer child, mix them -// into the base widget class. (This is a hack, but it's effective.) -dojo.extend(dijit._Widget, { - // selected: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // Specifies that this widget should be the initially displayed pane. - // Note: to change the selected child use `dijit.layout.StackContainer.selectChild` - selected: false, - - // closable: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // True if user can close (destroy) this child, such as (for example) clicking the X on the tab. - closable: false, - - // iconClass: String - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // CSS Class specifying icon to use in label associated with this pane. - iconClass: "", - - // showTitle: Boolean - // Parameter for children of `dijit.layout.StackContainer` or subclasses. - // When true, display title of this widget as tab label etc., rather than just using - // icon specified in iconClass - showTitle: true -}); - -} +//>>built +define("dijit/layout/StackContainer",["dojo/_base/array","dojo/cookie","dojo/_base/declare","dojo/dom-class","dojo/_base/kernel","dojo/_base/lang","dojo/ready","dojo/topic","../registry","../_WidgetBase","./_LayoutWidget","dojo/i18n!../nls/common"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b){if(!_5.isAsync){_7(0,function(){var _c=["dijit/layout/StackController"];require(_c);});}_6.extend(_a,{selected:false,closable:false,iconClass:"dijitNoIcon",showTitle:true});return _3("dijit.layout.StackContainer",_b,{doLayout:true,persist:false,baseClass:"dijitStackContainer",buildRendering:function(){this.inherited(arguments);_4.add(this.domNode,"dijitLayoutContainer");this.containerNode.setAttribute("role","tabpanel");},postCreate:function(){this.inherited(arguments);this.connect(this.domNode,"onkeypress",this._onKeyPress);},startup:function(){if(this._started){return;}var _d=this.getChildren();_1.forEach(_d,this._setupChild,this);if(this.persist){this.selectedChildWidget=_9.byId(_2(this.id+"_selectedChild"));}else{_1.some(_d,function(_e){if(_e.selected){this.selectedChildWidget=_e;}return _e.selected;},this);}var _f=this.selectedChildWidget;if(!_f&&_d[0]){_f=this.selectedChildWidget=_d[0];_f.selected=true;}_8.publish(this.id+"-startup",{children:_d,selected:_f});this.inherited(arguments);},resize:function(){if(!this._hasBeenShown){this._hasBeenShown=true;var _10=this.selectedChildWidget;if(_10){this._showChild(_10);}}this.inherited(arguments);},_setupChild:function(_11){this.inherited(arguments);_4.replace(_11.domNode,"dijitHidden","dijitVisible");_11.domNode.title="";},addChild:function(_12,_13){this.inherited(arguments);if(this._started){_8.publish(this.id+"-addChild",_12,_13);this.layout();if(!this.selectedChildWidget){this.selectChild(_12);}}},removeChild:function(_14){this.inherited(arguments);if(this._started){_8.publish(this.id+"-removeChild",_14);}if(this._descendantsBeingDestroyed){return;}if(this.selectedChildWidget===_14){this.selectedChildWidget=undefined;if(this._started){var _15=this.getChildren();if(_15.length){this.selectChild(_15[0]);}}}if(this._started){this.layout();}},selectChild:function(_16,_17){_16=_9.byId(_16);if(this.selectedChildWidget!=_16){var d=this._transition(_16,this.selectedChildWidget,_17);this._set("selectedChildWidget",_16);_8.publish(this.id+"-selectChild",_16);if(this.persist){_2(this.id+"_selectedChild",this.selectedChildWidget.id);}}return d;},_transition:function(_18,_19){if(_19){this._hideChild(_19);}var d=this._showChild(_18);if(_18.resize){if(this.doLayout){_18.resize(this._containerContentBox||this._contentBox);}else{_18.resize();}}return d;},_adjacent:function(_1a){var _1b=this.getChildren();var _1c=_1.indexOf(_1b,this.selectedChildWidget);_1c+=_1a?1:_1b.length-1;return _1b[_1c%_1b.length];},forward:function(){return this.selectChild(this._adjacent(true),true);},back:function(){return this.selectChild(this._adjacent(false),true);},_onKeyPress:function(e){_8.publish(this.id+"-containerKeyPress",{e:e,page:this});},layout:function(){var _1d=this.selectedChildWidget;if(_1d&&_1d.resize){if(this.doLayout){_1d.resize(this._containerContentBox||this._contentBox);}else{_1d.resize();}}},_showChild:function(_1e){var _1f=this.getChildren();_1e.isFirstChild=(_1e==_1f[0]);_1e.isLastChild=(_1e==_1f[_1f.length-1]);_1e._set("selected",true);_4.replace(_1e.domNode,"dijitVisible","dijitHidden");return (_1e._onShow&&_1e._onShow())||true;},_hideChild:function(_20){_20._set("selected",false);_4.replace(_20.domNode,"dijitHidden","dijitVisible");_20.onHide&&_20.onHide();},closeChild:function(_21){var _22=_21.onClose(this,_21);if(_22){this.removeChild(_21);_21.destroyRecursive();}},destroyDescendants:function(_23){this._descendantsBeingDestroyed=true;this.selectedChildWidget=undefined;_1.forEach(this.getChildren(),function(_24){if(!_23){this.removeChild(_24);}_24.destroyRecursive(_23);},this);this._descendantsBeingDestroyed=false;}});});
\ No newline at end of file diff --git a/lib/dijit/layout/StackContainer.js.uncompressed.js b/lib/dijit/layout/StackContainer.js.uncompressed.js new file mode 100644 index 000000000..fad72813b --- /dev/null +++ b/lib/dijit/layout/StackContainer.js.uncompressed.js @@ -0,0 +1,368 @@ +define("dijit/layout/StackContainer", [ + "dojo/_base/array", // array.forEach array.indexOf array.some + "dojo/cookie", // cookie + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add domClass.replace + "dojo/_base/kernel", // kernel.isAsync + "dojo/_base/lang", // lang.extend + "dojo/ready", + "dojo/topic", // publish + "../registry", // registry.byId + "../_WidgetBase", + "./_LayoutWidget", + "dojo/i18n!../nls/common" +], function(array, cookie, declare, domClass, kernel, lang, ready, topic, + registry, _WidgetBase, _LayoutWidget){ + +/*===== +var _WidgetBase = dijit._WidgetBase; +var _LayoutWidget = dijit.layout._LayoutWidget; +var StackController = dijit.layout.StackController; +=====*/ + +// module: +// dijit/layout/StackContainer +// summary: +// A container that has multiple children, but shows only one child at a time. + +// Back compat w/1.6, remove for 2.0 +if(!kernel.isAsync){ + ready(0, function(){ + var requires = ["dijit/layout/StackController"]; + require(requires); // use indirection so modules not rolled into a build + }); +} + +// These arguments can be specified for the children of a StackContainer. +// Since any widget can be specified as a StackContainer child, mix them +// into the base widget class. (This is a hack, but it's effective.) +lang.extend(_WidgetBase, { + // selected: Boolean + // Parameter for children of `dijit.layout.StackContainer` or subclasses. + // Specifies that this widget should be the initially displayed pane. + // Note: to change the selected child use `dijit.layout.StackContainer.selectChild` + selected: false, + + // closable: Boolean + // Parameter for children of `dijit.layout.StackContainer` or subclasses. + // True if user can close (destroy) this child, such as (for example) clicking the X on the tab. + closable: false, + + // iconClass: String + // Parameter for children of `dijit.layout.StackContainer` or subclasses. + // CSS Class specifying icon to use in label associated with this pane. + iconClass: "dijitNoIcon", + + // showTitle: Boolean + // Parameter for children of `dijit.layout.StackContainer` or subclasses. + // When true, display title of this widget as tab label etc., rather than just using + // icon specified in iconClass + showTitle: true +}); + +return declare("dijit.layout.StackContainer", _LayoutWidget, { + // summary: + // A container that has multiple children, but shows only + // one child at a time + // + // description: + // A container for widgets (ContentPanes, for example) That displays + // only one Widget at a time. + // + // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild + // + // Can be base class for container, Wizard, Show, etc. + + // doLayout: Boolean + // If true, change the size of my currently displayed child to match my size + doLayout: true, + + // persist: Boolean + // Remembers the selected child across sessions + persist: false, + + baseClass: "dijitStackContainer", + +/*===== + // selectedChildWidget: [readonly] dijit._Widget + // References the currently selected child widget, if any. + // Adjust selected child with selectChild() method. + selectedChildWidget: null, +=====*/ + + buildRendering: function(){ + this.inherited(arguments); + domClass.add(this.domNode, "dijitLayoutContainer"); + this.containerNode.setAttribute("role", "tabpanel"); + }, + + postCreate: function(){ + this.inherited(arguments); + this.connect(this.domNode, "onkeypress", this._onKeyPress); + }, + + startup: function(){ + if(this._started){ return; } + + var children = this.getChildren(); + + // Setup each page panel to be initially hidden + array.forEach(children, this._setupChild, this); + + // Figure out which child to initially display, defaulting to first one + if(this.persist){ + this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild")); + }else{ + array.some(children, function(child){ + if(child.selected){ + this.selectedChildWidget = child; + } + return child.selected; + }, this); + } + var selected = this.selectedChildWidget; + if(!selected && children[0]){ + selected = this.selectedChildWidget = children[0]; + selected.selected = true; + } + + // Publish information about myself so any StackControllers can initialize. + // This needs to happen before this.inherited(arguments) so that for + // TabContainer, this._contentBox doesn't include the space for the tab labels. + topic.publish(this.id+"-startup", {children: children, selected: selected}); + + // Startup each child widget, and do initial layout like setting this._contentBox, + // then calls this.resize() which does the initial sizing on the selected child. + this.inherited(arguments); + }, + + resize: function(){ + // Resize is called when we are first made visible (it's called from startup() + // if we are initially visible). If this is the first time we've been made + // visible then show our first child. + if(!this._hasBeenShown){ + this._hasBeenShown = true; + var selected = this.selectedChildWidget; + if(selected){ + this._showChild(selected); + } + } + this.inherited(arguments); + }, + + _setupChild: function(/*dijit._Widget*/ child){ + // Overrides _LayoutWidget._setupChild() + + this.inherited(arguments); + + domClass.replace(child.domNode, "dijitHidden", "dijitVisible"); + + // remove the title attribute so it doesn't show up when i hover + // over a node + child.domNode.title = ""; + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + // Overrides _Container.addChild() to do layout and publish events + + this.inherited(arguments); + + if(this._started){ + topic.publish(this.id+"-addChild", child, insertIndex); // publish + + // in case the tab titles have overflowed from one line to two lines + // (or, if this if first child, from zero lines to one line) + // TODO: w/ScrollingTabController this is no longer necessary, although + // ScrollTabController.resize() does need to get called to show/hide + // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild(). + // If this is updated to not layout [except for initial child added / last child removed], update + // "childless startup" test in StackContainer.html to check for no resize event after second addChild() + this.layout(); + + // if this is the first child, then select it + if(!this.selectedChildWidget){ + this.selectChild(child); + } + } + }, + + removeChild: function(/*dijit._Widget*/ page){ + // Overrides _Container.removeChild() to do layout and publish events + + this.inherited(arguments); + + if(this._started){ + // this will notify any tablists to remove a button; do this first because it may affect sizing + topic.publish(this.id + "-removeChild", page); // publish + } + + // If all our children are being destroyed than don't run the code below (to select another page), + // because we are deleting every page one by one + if(this._descendantsBeingDestroyed){ return; } + + // Select new page to display, also updating TabController to show the respective tab. + // Do this before layout call because it can affect the height of the TabController. + if(this.selectedChildWidget === page){ + this.selectedChildWidget = undefined; + if(this._started){ + var children = this.getChildren(); + if(children.length){ + this.selectChild(children[0]); + } + } + } + + if(this._started){ + // In case the tab titles now take up one line instead of two lines + // (note though that ScrollingTabController never overflows to multiple lines), + // or the height has changed slightly because of addition/removal of tab which close icon + this.layout(); + } + }, + + selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){ + // summary: + // Show the given widget (which must be one of my children) + // page: + // Reference to child widget or id of child widget + + page = registry.byId(page); + + if(this.selectedChildWidget != page){ + // Deselect old page and select new one + var d = this._transition(page, this.selectedChildWidget, animate); + this._set("selectedChildWidget", page); + topic.publish(this.id+"-selectChild", page); // publish + + if(this.persist){ + cookie(this.id + "_selectedChild", this.selectedChildWidget.id); + } + } + + return d; // If child has an href, promise that fires when the child's href finishes loading + }, + + _transition: function(newWidget, oldWidget /*===== , animate =====*/){ + // summary: + // Hide the old widget and display the new widget. + // Subclasses should override this. + // newWidget: dijit._Widget + // The newly selected widget. + // oldWidget: dijit._Widget + // The previously selected widget. + // animate: Boolean + // Used by AccordionContainer to turn on/off slide effect. + // tags: + // protected extension + if(oldWidget){ + this._hideChild(oldWidget); + } + var d = this._showChild(newWidget); + + // 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(newWidget.resize){ + if(this.doLayout){ + newWidget.resize(this._containerContentBox || this._contentBox); + }else{ + // the child should pick it's own size but we still need to call resize() + // (with no arguments) to let the widget lay itself out + newWidget.resize(); + } + } + + return d; // If child has an href, promise that fires when the child's href finishes loading + }, + + _adjacent: function(/*Boolean*/ forward){ + // summary: + // Gets the next/previous child widget in this container from the current selection. + var children = this.getChildren(); + var index = array.indexOf(children, this.selectedChildWidget); + index += forward ? 1 : children.length - 1; + return children[ index % children.length ]; // dijit._Widget + }, + + forward: function(){ + // summary: + // Advance to next page. + return this.selectChild(this._adjacent(true), true); + }, + + back: function(){ + // summary: + // Go back to previous page. + return this.selectChild(this._adjacent(false), true); + }, + + _onKeyPress: function(e){ + topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish + }, + + layout: function(){ + // Implement _LayoutWidget.layout() virtual method. + var child = this.selectedChildWidget; + if(child && child.resize){ + if(this.doLayout){ + child.resize(this._containerContentBox || this._contentBox); + }else{ + child.resize(); + } + } + }, + + _showChild: function(/*dijit._Widget*/ page){ + // summary: + // Show the specified child by changing it's CSS, and call _onShow()/onShow() so + // it can do any updates it needs regarding loading href's etc. + // returns: + // Promise that fires when page has finished showing, or true if there's no href + var children = this.getChildren(); + page.isFirstChild = (page == children[0]); + page.isLastChild = (page == children[children.length-1]); + page._set("selected", true); + + domClass.replace(page.domNode, "dijitVisible", "dijitHidden"); + + return (page._onShow && page._onShow()) || true; + }, + + _hideChild: function(/*dijit._Widget*/ page){ + // summary: + // Hide the specified child by changing it's CSS, and call _onHide() so + // it's notified. + page._set("selected", false); + domClass.replace(page.domNode, "dijitHidden", "dijitVisible"); + + page.onHide && page.onHide(); + }, + + closeChild: function(/*dijit._Widget*/ page){ + // summary: + // Callback when user clicks the [X] to remove a page. + // If onClose() returns true then remove and destroy the child. + // tags: + // private + var remove = page.onClose(this, page); + if(remove){ + this.removeChild(page); + // makes sure we can clean up executeScripts in ContentPane onUnLoad + page.destroyRecursive(); + } + }, + + destroyDescendants: function(/*Boolean*/ preserveDom){ + this._descendantsBeingDestroyed = true; + this.selectedChildWidget = undefined; + array.forEach(this.getChildren(), function(child){ + if(!preserveDom){ + this.removeChild(child); + } + child.destroyRecursive(preserveDom); + }, this); + this._descendantsBeingDestroyed = false; + } +}); + +}); diff --git a/lib/dijit/layout/StackController.js b/lib/dijit/layout/StackController.js index e0d2075bb..504a9d717 100644 --- a/lib/dijit/layout/StackController.js +++ b/lib/dijit/layout/StackController.js @@ -1,334 +1,2 @@ -/* - 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.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.StackController"] = true; -dojo.provide("dijit.layout.StackController"); -dojo.require("dijit._Widget"); -dojo.require("dijit._Templated"); -dojo.require("dijit._Container"); -dojo.require("dijit.form.ToggleButton"); -dojo.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"); - - -dojo.declare( - "dijit.layout.StackController", - [dijit._Widget, dijit._Templated, dijit._Container], - { - // summary: - // Set of buttons to select a page in a page list. - // description: - // Monitors the specified StackContainer, and whenever a page is - // added, deleted, or selected, updates itself accordingly. - - templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>", - - // containerId: [const] String - // The id of the page container that I point to - containerId: "", - - // buttonWidget: [const] String - // The name of the button widget to create to correspond to each page - buttonWidget: "dijit.layout._StackButton", - - constructor: function(){ - this.pane2button = {}; // mapping from pane id to buttons - this.pane2connects = {}; // mapping from pane id to this.connect() handles - this.pane2watches = {}; // mapping from pane id to watch() handles - }, - - buildRendering: function(){ - this.inherited(arguments); - dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above. - }, - - postCreate: function(){ - this.inherited(arguments); - - // Listen to notifications from StackContainer - this.subscribe(this.containerId+"-startup", "onStartup"); - this.subscribe(this.containerId+"-addChild", "onAddChild"); - this.subscribe(this.containerId+"-removeChild", "onRemoveChild"); - this.subscribe(this.containerId+"-selectChild", "onSelectChild"); - this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress"); - }, - - onStartup: function(/*Object*/ info){ - // summary: - // Called after StackContainer has finished initializing - // tags: - // private - dojo.forEach(info.children, this.onAddChild, this); - if(info.selected){ - // Show button corresponding to selected pane (unless selected - // is null because there are no panes) - this.onSelectChild(info.selected); - } - }, - - destroy: function(){ - for(var pane in this.pane2button){ - this.onRemoveChild(dijit.byId(pane)); - } - this.inherited(arguments); - }, - - onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){ - // summary: - // Called whenever a page is added to the container. - // Create button corresponding to the page. - // tags: - // private - - // create an instance of the button widget - var cls = dojo.getObject(this.buttonWidget); - var button = new cls({ - id: this.id + "_" + page.id, - label: page.title, - dir: page.dir, - lang: page.lang, - showLabel: page.showTitle, - iconClass: page.iconClass, - closeButton: page.closable, - title: page.tooltip - }); - dijit.setWaiState(button.focusNode,"selected", "false"); - - - // map from page attribute to corresponding tab button attribute - var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"], - buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"]; - - // watch() so events like page title changes are reflected in tab button - this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){ - return page.watch(pageAttr, function(name, oldVal, newVal){ - button.set(buttonAttrList[idx], newVal); - }); - }); - - // connections so that clicking a tab button selects the corresponding page - this.pane2connects[page.id] = [ - this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)), - this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page)) - ]; - - this.addChild(button, insertIndex); - this.pane2button[page.id] = button; - page.controlButton = button; // this value might be overwritten if two tabs point to same container - if(!this._currentChild){ // put the first child into the tab order - button.focusNode.setAttribute("tabIndex", "0"); - dijit.setWaiState(button.focusNode, "selected", "true"); - this._currentChild = page; - } - // make sure all tabs have the same length - if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){ - this._rectifyRtlTabList(); - } - }, - - onRemoveChild: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever a page is removed from the container. - // Remove the button corresponding to the page. - // tags: - // private - - if(this._currentChild === page){ this._currentChild = null; } - - // disconnect/unwatch connections/watches related to page being removed - dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect")); - delete this.pane2connects[page.id]; - dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); }); - delete this.pane2watches[page.id]; - - var button = this.pane2button[page.id]; - if(button){ - this.removeChild(button); - delete this.pane2button[page.id]; - button.destroy(); - } - delete page.controlButton; - }, - - onSelectChild: function(/*dijit._Widget*/ page){ - // summary: - // Called when a page has been selected in the StackContainer, either by me or by another StackController - // tags: - // private - - if(!page){ return; } - - if(this._currentChild){ - var oldButton=this.pane2button[this._currentChild.id]; - oldButton.set('checked', false); - dijit.setWaiState(oldButton.focusNode, "selected", "false"); - oldButton.focusNode.setAttribute("tabIndex", "-1"); - } - - var newButton=this.pane2button[page.id]; - newButton.set('checked', true); - dijit.setWaiState(newButton.focusNode, "selected", "true"); - this._currentChild = page; - newButton.focusNode.setAttribute("tabIndex", "0"); - var container = dijit.byId(this.containerId); - dijit.setWaiState(container.containerNode, "labelledby", newButton.id); - }, - - onButtonClick: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever one of my child buttons is pressed in an attempt to select a page - // tags: - // private - - var container = dijit.byId(this.containerId); - container.selectChild(page); - }, - - onCloseButtonClick: function(/*dijit._Widget*/ page){ - // summary: - // Called whenever one of my child buttons [X] is pressed in an attempt to close a page - // tags: - // private - - var container = dijit.byId(this.containerId); - container.closeChild(page); - if(this._currentChild){ - var b = this.pane2button[this._currentChild.id]; - if(b){ - dijit.focus(b.focusNode || b.domNode); - } - } - }, - - // TODO: this is a bit redundant with forward, back api in StackContainer - adjacent: function(/*Boolean*/ forward){ - // summary: - // Helper for onkeypress to find next/previous button - // tags: - // private - - if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; } - // find currently focused button in children array - var children = this.getChildren(); - var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]); - // pick next button to focus on - var offset = forward ? 1 : children.length - 1; - return children[ (current + offset) % children.length ]; // dijit._Widget - }, - - onkeypress: function(/*Event*/ e){ - // summary: - // Handle keystrokes on the page list, for advancing to next/previous button - // and closing the current page if the page is closable. - // tags: - // private - - if(this.disabled || e.altKey ){ return; } - var forward = null; - if(e.ctrlKey || !e._djpage){ - var k = dojo.keys; - switch(e.charOrCode){ - case k.LEFT_ARROW: - case k.UP_ARROW: - if(!e._djpage){ forward = false; } - break; - case k.PAGE_UP: - if(e.ctrlKey){ forward = false; } - break; - case k.RIGHT_ARROW: - case k.DOWN_ARROW: - if(!e._djpage){ forward = true; } - break; - case k.PAGE_DOWN: - if(e.ctrlKey){ forward = true; } - break; - case k.HOME: - case k.END: - var children = this.getChildren(); - if(children && children.length){ - children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick(); - } - dojo.stopEvent(e); - break; - case k.DELETE: - if(this._currentChild.closable){ - this.onCloseButtonClick(this._currentChild); - } - dojo.stopEvent(e); - break; - default: - if(e.ctrlKey){ - if(e.charOrCode === k.TAB){ - this.adjacent(!e.shiftKey).onClick(); - dojo.stopEvent(e); - }else if(e.charOrCode == "w"){ - if(this._currentChild.closable){ - this.onCloseButtonClick(this._currentChild); - } - dojo.stopEvent(e); // avoid browser tab closing. - } - } - } - // handle next/previous page navigation (left/right arrow, etc.) - if(forward !== null){ - this.adjacent(forward).onClick(); - dojo.stopEvent(e); - } - } - }, - - onContainerKeyPress: function(/*Object*/ info){ - // summary: - // Called when there was a keypress on the container - // tags: - // private - info.e._djpage = info.page; - this.onkeypress(info.e); - } - }); - - -dojo.declare("dijit.layout._StackButton", - dijit.form.ToggleButton, - { - // summary: - // Internal widget used by StackContainer. - // description: - // The button-like or tab-like object you click to select or delete a page - // tags: - // private - - // Override _FormWidget.tabIndex. - // StackContainer buttons are not in the tab order by default. - // Probably we should be calling this.startupKeyNavChildren() instead. - tabIndex: "-1", - - buildRendering: function(/*Event*/ evt){ - this.inherited(arguments); - dijit.setWaiRole((this.focusNode || this.domNode), "tab"); - }, - - onClick: function(/*Event*/ evt){ - // summary: - // This is for TabContainer where the tabs are <span> rather than button, - // so need to set focus explicitly (on some browsers) - // Note that you shouldn't override this method, but you can connect to it. - dijit.focus(this.focusNode); - - // ... now let StackController catch the event and tell me what to do - }, - - onClickCloseButton: function(/*Event*/ evt){ - // summary: - // StackContainer connects to this function; if your widget contains a close button - // then clicking it should call this function. - // Note that you shouldn't override this method, but you can connect to it. - evt.stopPropagation(); - } - }); - -} +//>>built +define("dijit/layout/StackController",["dojo/_base/array","dojo/_base/declare","dojo/_base/event","dojo/keys","dojo/_base/lang","dojo/_base/sniff","../focus","../registry","../_Widget","../_TemplatedMixin","../_Container","../form/ToggleButton","dojo/i18n!../nls/common"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c){var _d=_2("dijit.layout._StackButton",_c,{tabIndex:"-1",closeButton:false,_setCheckedAttr:function(_e,_f){this.inherited(arguments);this.focusNode.removeAttribute("aria-pressed");},buildRendering:function(evt){this.inherited(arguments);(this.focusNode||this.domNode).setAttribute("role","tab");},onClick:function(){_7.focus(this.focusNode);},onClickCloseButton:function(evt){evt.stopPropagation();}});var _10=_2("dijit.layout.StackController",[_9,_a,_b],{baseClass:"dijitStackController",templateString:"<span role='tablist' data-dojo-attach-event='onkeypress'></span>",containerId:"",buttonWidget:_d,constructor:function(){this.pane2button={};this.pane2connects={};this.pane2watches={};},postCreate:function(){this.inherited(arguments);this.subscribe(this.containerId+"-startup","onStartup");this.subscribe(this.containerId+"-addChild","onAddChild");this.subscribe(this.containerId+"-removeChild","onRemoveChild");this.subscribe(this.containerId+"-selectChild","onSelectChild");this.subscribe(this.containerId+"-containerKeyPress","onContainerKeyPress");},onStartup:function(_11){_1.forEach(_11.children,this.onAddChild,this);if(_11.selected){this.onSelectChild(_11.selected);}},destroy:function(){for(var _12 in this.pane2button){this.onRemoveChild(_8.byId(_12));}this.inherited(arguments);},onAddChild:function(_13,_14){var cls=_5.isString(this.buttonWidget)?_5.getObject(this.buttonWidget):this.buttonWidget;var _15=new cls({id:this.id+"_"+_13.id,label:_13.title,dir:_13.dir,lang:_13.lang,textDir:_13.textDir,showLabel:_13.showTitle,iconClass:_13.iconClass,closeButton:_13.closable,title:_13.tooltip});_15.focusNode.setAttribute("aria-selected","false");var _16=["title","showTitle","iconClass","closable","tooltip"],_17=["label","showLabel","iconClass","closeButton","title"];this.pane2watches[_13.id]=_1.map(_16,function(_18,idx){return _13.watch(_18,function(_19,_1a,_1b){_15.set(_17[idx],_1b);});});this.pane2connects[_13.id]=[this.connect(_15,"onClick",_5.hitch(this,"onButtonClick",_13)),this.connect(_15,"onClickCloseButton",_5.hitch(this,"onCloseButtonClick",_13))];this.addChild(_15,_14);this.pane2button[_13.id]=_15;_13.controlButton=_15;if(!this._currentChild){_15.focusNode.setAttribute("tabIndex","0");_15.focusNode.setAttribute("aria-selected","true");this._currentChild=_13;}if(!this.isLeftToRight()&&_6("ie")&&this._rectifyRtlTabList){this._rectifyRtlTabList();}},onRemoveChild:function(_1c){if(this._currentChild===_1c){this._currentChild=null;}_1.forEach(this.pane2connects[_1c.id],_5.hitch(this,"disconnect"));delete this.pane2connects[_1c.id];_1.forEach(this.pane2watches[_1c.id],function(w){w.unwatch();});delete this.pane2watches[_1c.id];var _1d=this.pane2button[_1c.id];if(_1d){this.removeChild(_1d);delete this.pane2button[_1c.id];_1d.destroy();}delete _1c.controlButton;},onSelectChild:function(_1e){if(!_1e){return;}if(this._currentChild){var _1f=this.pane2button[this._currentChild.id];_1f.set("checked",false);_1f.focusNode.setAttribute("aria-selected","false");_1f.focusNode.setAttribute("tabIndex","-1");}var _20=this.pane2button[_1e.id];_20.set("checked",true);_20.focusNode.setAttribute("aria-selected","true");this._currentChild=_1e;_20.focusNode.setAttribute("tabIndex","0");var _21=_8.byId(this.containerId);_21.containerNode.setAttribute("aria-labelledby",_20.id);},onButtonClick:function(_22){if(this._currentChild.id===_22.id){var _23=this.pane2button[_22.id];_23.set("checked",true);}var _24=_8.byId(this.containerId);_24.selectChild(_22);},onCloseButtonClick:function(_25){var _26=_8.byId(this.containerId);_26.closeChild(_25);if(this._currentChild){var b=this.pane2button[this._currentChild.id];if(b){_7.focus(b.focusNode||b.domNode);}}},adjacent:function(_27){if(!this.isLeftToRight()&&(!this.tabPosition||/top|bottom/.test(this.tabPosition))){_27=!_27;}var _28=this.getChildren();var _29=_1.indexOf(_28,this.pane2button[this._currentChild.id]);var _2a=_27?1:_28.length-1;return _28[(_29+_2a)%_28.length];},onkeypress:function(e){if(this.disabled||e.altKey){return;}var _2b=null;if(e.ctrlKey||!e._djpage){switch(e.charOrCode){case _4.LEFT_ARROW:case _4.UP_ARROW:if(!e._djpage){_2b=false;}break;case _4.PAGE_UP:if(e.ctrlKey){_2b=false;}break;case _4.RIGHT_ARROW:case _4.DOWN_ARROW:if(!e._djpage){_2b=true;}break;case _4.PAGE_DOWN:if(e.ctrlKey){_2b=true;}break;case _4.HOME:case _4.END:var _2c=this.getChildren();if(_2c&&_2c.length){_2c[e.charOrCode==_4.HOME?0:_2c.length-1].onClick();}_3.stop(e);break;case _4.DELETE:if(this._currentChild.closable){this.onCloseButtonClick(this._currentChild);}_3.stop(e);break;default:if(e.ctrlKey){if(e.charOrCode===_4.TAB){this.adjacent(!e.shiftKey).onClick();_3.stop(e);}else{if(e.charOrCode=="w"){if(this._currentChild.closable){this.onCloseButtonClick(this._currentChild);}_3.stop(e);}}}}if(_2b!==null){this.adjacent(_2b).onClick();_3.stop(e);}}},onContainerKeyPress:function(_2d){_2d.e._djpage=_2d.page;this.onkeypress(_2d.e);}});_10.StackButton=_d;return _10;});
\ No newline at end of file diff --git a/lib/dijit/layout/StackController.js.uncompressed.js b/lib/dijit/layout/StackController.js.uncompressed.js new file mode 100644 index 000000000..8e5432cdd --- /dev/null +++ b/lib/dijit/layout/StackController.js.uncompressed.js @@ -0,0 +1,356 @@ +define("dijit/layout/StackController", [ + "dojo/_base/array", // array.forEach array.indexOf array.map + "dojo/_base/declare", // declare + "dojo/_base/event", // event.stop + "dojo/keys", // keys + "dojo/_base/lang", // lang.getObject + "dojo/_base/sniff", // has("ie") + "../focus", // focus.focus() + "../registry", // registry.byId + "../_Widget", + "../_TemplatedMixin", + "../_Container", + "../form/ToggleButton", + "dojo/i18n!../nls/common" +], function(array, declare, event, keys, lang, has, + focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){ + +/*===== + var _Widget = dijit._Widget; + var _TemplatedMixin = dijit._TemplatedMixin; + var _Container = dijit._Container; + var ToggleButton = dijit.form.ToggleButton; +=====*/ + + // module: + // dijit/layout/StackController + // summary: + // Set of buttons to select a page in a `dijit.layout.StackContainer` + + var StackButton = declare("dijit.layout._StackButton", ToggleButton, { + // summary: + // Internal widget used by StackContainer. + // description: + // The button-like or tab-like object you click to select or delete a page + // tags: + // private + + // Override _FormWidget.tabIndex. + // StackContainer buttons are not in the tab order by default. + // Probably we should be calling this.startupKeyNavChildren() instead. + tabIndex: "-1", + + // closeButton: Boolean + // When true, display close button for this tab + closeButton: false, + + _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){ + this.inherited(arguments); + this.focusNode.removeAttribute("aria-pressed"); + }, + + buildRendering: function(/*Event*/ evt){ + this.inherited(arguments); + (this.focusNode || this.domNode).setAttribute("role", "tab"); + }, + + onClick: function(/*Event*/ /*===== evt =====*/){ + // summary: + // This is for TabContainer where the tabs are <span> rather than button, + // so need to set focus explicitly (on some browsers) + // Note that you shouldn't override this method, but you can connect to it. + focus.focus(this.focusNode); + + // ... now let StackController catch the event and tell me what to do + }, + + onClickCloseButton: function(/*Event*/ evt){ + // summary: + // StackContainer connects to this function; if your widget contains a close button + // then clicking it should call this function. + // Note that you shouldn't override this method, but you can connect to it. + evt.stopPropagation(); + } + }); + + + var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], { + // summary: + // Set of buttons to select a page in a `dijit.layout.StackContainer` + // description: + // Monitors the specified StackContainer, and whenever a page is + // added, deleted, or selected, updates itself accordingly. + + baseClass: "dijitStackController", + + templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>", + + // containerId: [const] String + // The id of the page container that I point to + containerId: "", + + // buttonWidget: [const] Constructor + // The button widget to create to correspond to each page + buttonWidget: StackButton, + + constructor: function(){ + this.pane2button = {}; // mapping from pane id to buttons + this.pane2connects = {}; // mapping from pane id to this.connect() handles + this.pane2watches = {}; // mapping from pane id to watch() handles + }, + + postCreate: function(){ + this.inherited(arguments); + + // Listen to notifications from StackContainer + this.subscribe(this.containerId+"-startup", "onStartup"); + this.subscribe(this.containerId+"-addChild", "onAddChild"); + this.subscribe(this.containerId+"-removeChild", "onRemoveChild"); + this.subscribe(this.containerId+"-selectChild", "onSelectChild"); + this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress"); + }, + + onStartup: function(/*Object*/ info){ + // summary: + // Called after StackContainer has finished initializing + // tags: + // private + array.forEach(info.children, this.onAddChild, this); + if(info.selected){ + // Show button corresponding to selected pane (unless selected + // is null because there are no panes) + this.onSelectChild(info.selected); + } + }, + + destroy: function(){ + for(var pane in this.pane2button){ + this.onRemoveChild(registry.byId(pane)); + } + this.inherited(arguments); + }, + + onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){ + // summary: + // Called whenever a page is added to the container. + // Create button corresponding to the page. + // tags: + // private + + // create an instance of the button widget + // (remove typeof buttonWidget == string support in 2.0) + var cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget; + var button = new cls({ + id: this.id + "_" + page.id, + label: page.title, + dir: page.dir, + lang: page.lang, + textDir: page.textDir, + showLabel: page.showTitle, + iconClass: page.iconClass, + closeButton: page.closable, + title: page.tooltip + }); + button.focusNode.setAttribute("aria-selected", "false"); + + + // map from page attribute to corresponding tab button attribute + var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"], + buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"]; + + // watch() so events like page title changes are reflected in tab button + this.pane2watches[page.id] = array.map(pageAttrList, function(pageAttr, idx){ + return page.watch(pageAttr, function(name, oldVal, newVal){ + button.set(buttonAttrList[idx], newVal); + }); + }); + + // connections so that clicking a tab button selects the corresponding page + this.pane2connects[page.id] = [ + this.connect(button, 'onClick', lang.hitch(this,"onButtonClick", page)), + this.connect(button, 'onClickCloseButton', lang.hitch(this,"onCloseButtonClick", page)) + ]; + + this.addChild(button, insertIndex); + this.pane2button[page.id] = button; + page.controlButton = button; // this value might be overwritten if two tabs point to same container + if(!this._currentChild){ // put the first child into the tab order + button.focusNode.setAttribute("tabIndex", "0"); + button.focusNode.setAttribute("aria-selected", "true"); + this._currentChild = page; + } + // make sure all tabs have the same length + if(!this.isLeftToRight() && has("ie") && this._rectifyRtlTabList){ + this._rectifyRtlTabList(); + } + }, + + onRemoveChild: function(/*dijit._Widget*/ page){ + // summary: + // Called whenever a page is removed from the container. + // Remove the button corresponding to the page. + // tags: + // private + + if(this._currentChild === page){ this._currentChild = null; } + + // disconnect/unwatch connections/watches related to page being removed + array.forEach(this.pane2connects[page.id], lang.hitch(this, "disconnect")); + delete this.pane2connects[page.id]; + array.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); }); + delete this.pane2watches[page.id]; + + var button = this.pane2button[page.id]; + if(button){ + this.removeChild(button); + delete this.pane2button[page.id]; + button.destroy(); + } + delete page.controlButton; + }, + + onSelectChild: function(/*dijit._Widget*/ page){ + // summary: + // Called when a page has been selected in the StackContainer, either by me or by another StackController + // tags: + // private + + if(!page){ return; } + + if(this._currentChild){ + var oldButton=this.pane2button[this._currentChild.id]; + oldButton.set('checked', false); + oldButton.focusNode.setAttribute("aria-selected", "false"); + oldButton.focusNode.setAttribute("tabIndex", "-1"); + } + + var newButton=this.pane2button[page.id]; + newButton.set('checked', true); + newButton.focusNode.setAttribute("aria-selected", "true"); + this._currentChild = page; + newButton.focusNode.setAttribute("tabIndex", "0"); + var container = registry.byId(this.containerId); + container.containerNode.setAttribute("aria-labelledby", newButton.id); + }, + + onButtonClick: function(/*dijit._Widget*/ page){ + // summary: + // Called whenever one of my child buttons is pressed in an attempt to select a page + // tags: + // private + + if(this._currentChild.id === page.id) { + //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page. + var button=this.pane2button[page.id]; + button.set('checked', true); + } + var container = registry.byId(this.containerId); + container.selectChild(page); + }, + + onCloseButtonClick: function(/*dijit._Widget*/ page){ + // summary: + // Called whenever one of my child buttons [X] is pressed in an attempt to close a page + // tags: + // private + + var container = registry.byId(this.containerId); + container.closeChild(page); + if(this._currentChild){ + var b = this.pane2button[this._currentChild.id]; + if(b){ + focus.focus(b.focusNode || b.domNode); + } + } + }, + + // TODO: this is a bit redundant with forward, back api in StackContainer + adjacent: function(/*Boolean*/ forward){ + // summary: + // Helper for onkeypress to find next/previous button + // tags: + // private + + if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; } + // find currently focused button in children array + var children = this.getChildren(); + var current = array.indexOf(children, this.pane2button[this._currentChild.id]); + // pick next button to focus on + var offset = forward ? 1 : children.length - 1; + return children[ (current + offset) % children.length ]; // dijit._Widget + }, + + onkeypress: function(/*Event*/ e){ + // summary: + // Handle keystrokes on the page list, for advancing to next/previous button + // and closing the current page if the page is closable. + // tags: + // private + + if(this.disabled || e.altKey ){ return; } + var forward = null; + if(e.ctrlKey || !e._djpage){ + switch(e.charOrCode){ + case keys.LEFT_ARROW: + case keys.UP_ARROW: + if(!e._djpage){ forward = false; } + break; + case keys.PAGE_UP: + if(e.ctrlKey){ forward = false; } + break; + case keys.RIGHT_ARROW: + case keys.DOWN_ARROW: + if(!e._djpage){ forward = true; } + break; + case keys.PAGE_DOWN: + if(e.ctrlKey){ forward = true; } + break; + case keys.HOME: + case keys.END: + var children = this.getChildren(); + if(children && children.length){ + children[e.charOrCode == keys.HOME ? 0 : children.length-1].onClick(); + } + event.stop(e); + break; + case keys.DELETE: + if(this._currentChild.closable){ + this.onCloseButtonClick(this._currentChild); + } + event.stop(e); + break; + default: + if(e.ctrlKey){ + if(e.charOrCode === keys.TAB){ + this.adjacent(!e.shiftKey).onClick(); + event.stop(e); + }else if(e.charOrCode == "w"){ + if(this._currentChild.closable){ + this.onCloseButtonClick(this._currentChild); + } + event.stop(e); // avoid browser tab closing. + } + } + } + // handle next/previous page navigation (left/right arrow, etc.) + if(forward !== null){ + this.adjacent(forward).onClick(); + event.stop(e); + } + } + }, + + onContainerKeyPress: function(/*Object*/ info){ + // summary: + // Called when there was a keypress on the container + // tags: + // private + info.e._djpage = info.page; + this.onkeypress(info.e); + } + }); + + StackController.StackButton = StackButton; // for monkey patching + + return StackController; +}); diff --git a/lib/dijit/layout/TabContainer.js b/lib/dijit/layout/TabContainer.js index 009422464..a78ec4422 100644 --- a/lib/dijit/layout/TabContainer.js +++ b/lib/dijit/layout/TabContainer.js @@ -1,76 +1,2 @@ -/* - 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.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.TabContainer"] = true; -dojo.provide("dijit.layout.TabContainer"); -dojo.require("dijit.layout._TabContainerBase"); -dojo.require("dijit.layout.TabController"); -dojo.require("dijit.layout.ScrollingTabController"); - - -dojo.declare("dijit.layout.TabContainer", - dijit.layout._TabContainerBase, - { - // summary: - // A Container with tabs to select each child (only one of which is displayed at a time). - // description: - // A TabContainer is a container that has multiple panes, but shows only - // one pane at a time. There are a set of tabs corresponding to each pane, - // where each tab has the name (aka title) of the pane, and optionally a close button. - - // useMenu: [const] Boolean - // True if a menu should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useMenu: true, - - // useSlider: [const] Boolean - // True if a slider should be used to select tabs when they are too - // wide to fit the TabContainer, false otherwise. - useSlider: true, - - // controllerWidget: String - // An optional parameter to override the widget used to display the tab labels - controllerWidget: "", - - _makeController: function(/*DomNode*/ srcNode){ - // summary: - // Instantiate tablist controller widget and return reference to it. - // Callback from _TabContainerBase.postCreate(). - // tags: - // protected extension - - var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"), - TabController = dojo.getObject(this.controllerWidget); - - return new TabController({ - id: this.id + "_tablist", - dir: this.dir, - lang: this.lang, - tabPosition: this.tabPosition, - doLayout: this.doLayout, - containerId: this.id, - "class": cls, - nested: this.nested, - useMenu: this.useMenu, - useSlider: this.useSlider, - tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null - }, srcNode); - }, - - postMixInProperties: function(){ - this.inherited(arguments); - - // Scrolling controller only works for horizontal non-nested tabs - if(!this.controllerWidget){ - this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ? - "dijit.layout.ScrollingTabController" : "dijit.layout.TabController"; - } - } -}); - -} +//>>built +define("dijit/layout/TabContainer",["dojo/_base/lang","dojo/_base/declare","./_TabContainerBase","./TabController","./ScrollingTabController"],function(_1,_2,_3,_4,_5){return _2("dijit.layout.TabContainer",_3,{useMenu:true,useSlider:true,controllerWidget:"",_makeController:function(_6){var _7=this.baseClass+"-tabs"+(this.doLayout?"":" dijitTabNoLayout"),_4=_1.getObject(this.controllerWidget);return new _4({id:this.id+"_tablist",dir:this.dir,lang:this.lang,textDir:this.textDir,tabPosition:this.tabPosition,doLayout:this.doLayout,containerId:this.id,"class":_7,nested:this.nested,useMenu:this.useMenu,useSlider:this.useSlider,tabStripClass:this.tabStrip?this.baseClass+(this.tabStrip?"":"No")+"Strip":null},_6);},postMixInProperties:function(){this.inherited(arguments);if(!this.controllerWidget){this.controllerWidget=(this.tabPosition=="top"||this.tabPosition=="bottom")&&!this.nested?"dijit.layout.ScrollingTabController":"dijit.layout.TabController";}}});});
\ No newline at end of file diff --git a/lib/dijit/layout/TabContainer.js.uncompressed.js b/lib/dijit/layout/TabContainer.js.uncompressed.js new file mode 100644 index 000000000..109ace0f1 --- /dev/null +++ b/lib/dijit/layout/TabContainer.js.uncompressed.js @@ -0,0 +1,79 @@ +define("dijit/layout/TabContainer", [ + "dojo/_base/lang", // lang.getObject + "dojo/_base/declare", // declare + "./_TabContainerBase", + "./TabController", + "./ScrollingTabController" +], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){ + +/*===== + var _TabContainerBase = dijit.layout._TabContainerBase; + var TabController = dijit.layout.TabController; + var ScrollingTabController = dijit.layout.ScrollingTabController; +=====*/ + + // module: + // dijit/layout/TabContainer + // summary: + // A Container with tabs to select each child (only one of which is displayed at a time). + + + return declare("dijit.layout.TabContainer", _TabContainerBase, { + // summary: + // A Container with tabs to select each child (only one of which is displayed at a time). + // description: + // A TabContainer is a container that has multiple panes, but shows only + // one pane at a time. There are a set of tabs corresponding to each pane, + // where each tab has the name (aka title) of the pane, and optionally a close button. + + // useMenu: [const] Boolean + // True if a menu should be used to select tabs when they are too + // wide to fit the TabContainer, false otherwise. + useMenu: true, + + // useSlider: [const] Boolean + // True if a slider should be used to select tabs when they are too + // wide to fit the TabContainer, false otherwise. + useSlider: true, + + // controllerWidget: String + // An optional parameter to override the widget used to display the tab labels + controllerWidget: "", + + _makeController: function(/*DomNode*/ srcNode){ + // summary: + // Instantiate tablist controller widget and return reference to it. + // Callback from _TabContainerBase.postCreate(). + // tags: + // protected extension + + var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"), + TabController = lang.getObject(this.controllerWidget); + + return new TabController({ + id: this.id + "_tablist", + dir: this.dir, + lang: this.lang, + textDir: this.textDir, + tabPosition: this.tabPosition, + doLayout: this.doLayout, + containerId: this.id, + "class": cls, + nested: this.nested, + useMenu: this.useMenu, + useSlider: this.useSlider, + tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null + }, srcNode); + }, + + postMixInProperties: function(){ + this.inherited(arguments); + + // Scrolling controller only works for horizontal non-nested tabs + if(!this.controllerWidget){ + this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ? + "dijit.layout.ScrollingTabController" : "dijit.layout.TabController"; + } + } + }); +}); diff --git a/lib/dijit/layout/TabController.js b/lib/dijit/layout/TabController.js index 0a2276aca..28c26d91e 100644 --- a/lib/dijit/layout/TabController.js +++ b/lib/dijit/layout/TabController.js @@ -1,161 +1,2 @@ -/* - 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.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout.TabController"] = true; -dojo.provide("dijit.layout.TabController"); -dojo.require("dijit.layout.StackController"); -dojo.require("dijit.Menu"); -dojo.require("dijit.MenuItem"); -dojo.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"); - - -// Menu is used for an accessible close button, would be nice to have a lighter-weight solution - - -dojo.declare("dijit.layout.TabController", - dijit.layout.StackController, -{ - // summary: - // Set of tabs (the things with titles and a close button, that you click to show a tab panel). - // Used internally by `dijit.layout.TabContainer`. - // description: - // Lets the user select the currently shown pane in a TabContainer or StackContainer. - // TabController also monitors the TabContainer, and whenever a pane is - // added or deleted updates itself accordingly. - // tags: - // private - - templateString: "<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>", - - // tabPosition: String - // Defines where tabs go relative to the content. - // "top", "bottom", "left-h", "right-h" - tabPosition: "top", - - // buttonWidget: String - // The name of the tab widget to create to correspond to each page - buttonWidget: "dijit.layout._TabButton", - - _rectifyRtlTabList: function(){ - // summary: - // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE - - if(0 >= this.tabPosition.indexOf('-h')){ return; } - if(!this.pane2button){ return; } - - var maxWidth = 0; - for(var pane in this.pane2button){ - var ow = this.pane2button[pane].innerDiv.scrollWidth; - maxWidth = Math.max(maxWidth, ow); - } - //unify the length of all the tabs - for(pane in this.pane2button){ - this.pane2button[pane].innerDiv.style.width = maxWidth + 'px'; - } - } -}); - -dojo.declare("dijit.layout._TabButton", - dijit.layout._StackButton, - { - // summary: - // A tab (the thing you click to select a pane). - // description: - // Contains the title of the pane, and optionally a close-button to destroy the pane. - // This is an internal widget and should not be instantiated directly. - // tags: - // private - - // baseClass: String - // The CSS class applied to the domNode. - baseClass: "dijitTab", - - // Apply dijitTabCloseButtonHover when close button is hovered - cssStateNodes: { - closeNode: "dijitTabCloseButton" - }, - - templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div role=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n \t<div role=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" dojoAttachPoint='iconNode' />\n\t\t <span dojoAttachPoint='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" dojoAttachPoint='closeNode'\n\t\t \t\tdojoAttachEvent='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span dojoAttachPoint='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"), - - // Override _FormWidget.scrollOnFocus. - // Don't scroll the whole tab container into view when the button is focused. - scrollOnFocus: false, - - buildRendering: function(){ - this.inherited(arguments); - - dojo.setSelectable(this.containerNode, false); - }, - - startup: function(){ - this.inherited(arguments); - var n = this.domNode; - - // Required to give IE6 a kick, as it initially hides the - // tabs until they are focused on. - setTimeout(function(){ - n.className = n.className; - }, 1); - }, - - _setCloseButtonAttr: function(/*Boolean*/ disp){ - // summary: - // Hide/show close button - this._set("closeButton", disp); - dojo.toggleClass(this.innerDiv, "dijitClosable", disp); - this.closeNode.style.display = disp ? "" : "none"; - if(disp){ - var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); - if(this.closeNode){ - dojo.attr(this.closeNode,"title", _nlsResources.itemClose); - } - // add context menu onto title button - var _nlsResources = dojo.i18n.getLocalization("dijit", "common"); - this._closeMenu = new dijit.Menu({ - id: this.id+"_Menu", - dir: this.dir, - lang: this.lang, - targetNodeIds: [this.domNode] - }); - - this._closeMenu.addChild(new dijit.MenuItem({ - label: _nlsResources.itemClose, - dir: this.dir, - lang: this.lang, - onClick: dojo.hitch(this, "onClickCloseButton") - })); - }else{ - if(this._closeMenu){ - this._closeMenu.destroyRecursive(); - delete this._closeMenu; - } - } - }, - _setLabelAttr: function(/*String*/ content){ - // summary: - // Hook for set('label', ...) to work. - // description: - // takes an HTML string. - // Inherited ToggleButton implementation will Set the label (text) of the button; - // Need to set the alt attribute of icon on tab buttons if no label displayed - this.inherited(arguments); - if(this.showLabel == false && !this.params.title){ - this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || ''); - } - }, - - destroy: function(){ - if(this._closeMenu){ - this._closeMenu.destroyRecursive(); - delete this._closeMenu; - } - this.inherited(arguments); - } -}); - -} +//>>built +require({cache:{"url:dijit/layout/templates/_TabButton.html":"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"}});define("dijit/layout/TabController",["dojo/_base/declare","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/i18n","dojo/_base/lang","./StackController","../Menu","../MenuItem","dojo/text!./templates/_TabButton.html","dojo/i18n!../nls/common"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a){var _b=_1("dijit.layout._TabButton",_7.StackButton,{baseClass:"dijitTab",cssStateNodes:{closeNode:"dijitTabCloseButton"},templateString:_a,scrollOnFocus:false,buildRendering:function(){this.inherited(arguments);_2.setSelectable(this.containerNode,false);},startup:function(){this.inherited(arguments);var n=this.domNode;setTimeout(function(){n.className=n.className;},1);},_setCloseButtonAttr:function(_c){this._set("closeButton",_c);_4.toggle(this.innerDiv,"dijitClosable",_c);this.closeNode.style.display=_c?"":"none";if(_c){var _d=_5.getLocalization("dijit","common");if(this.closeNode){_3.set(this.closeNode,"title",_d.itemClose);}this._closeMenu=new _8({id:this.id+"_Menu",dir:this.dir,lang:this.lang,textDir:this.textDir,targetNodeIds:[this.domNode]});this._closeMenu.addChild(new _9({label:_d.itemClose,dir:this.dir,lang:this.lang,textDir:this.textDir,onClick:_6.hitch(this,"onClickCloseButton")}));}else{if(this._closeMenu){this._closeMenu.destroyRecursive();delete this._closeMenu;}}},_setLabelAttr:function(_e){this.inherited(arguments);if(!this.showLabel&&!this.params.title){this.iconNode.alt=_6.trim(this.containerNode.innerText||this.containerNode.textContent||"");}},destroy:function(){if(this._closeMenu){this._closeMenu.destroyRecursive();delete this._closeMenu;}this.inherited(arguments);}});var _f=_1("dijit.layout.TabController",_7,{baseClass:"dijitTabController",templateString:"<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",tabPosition:"top",buttonWidget:_b,_rectifyRtlTabList:function(){if(0>=this.tabPosition.indexOf("-h")){return;}if(!this.pane2button){return;}var _10=0;for(var _11 in this.pane2button){var ow=this.pane2button[_11].innerDiv.scrollWidth;_10=Math.max(_10,ow);}for(_11 in this.pane2button){this.pane2button[_11].innerDiv.style.width=_10+"px";}}});_f.TabButton=_b;return _f;});
\ No newline at end of file diff --git a/lib/dijit/layout/TabController.js.uncompressed.js b/lib/dijit/layout/TabController.js.uncompressed.js new file mode 100644 index 000000000..d62a4038d --- /dev/null +++ b/lib/dijit/layout/TabController.js.uncompressed.js @@ -0,0 +1,172 @@ +require({cache:{ +'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"}}); +define("dijit/layout/TabController", [ + "dojo/_base/declare", // declare + "dojo/dom", // dom.setSelectable + "dojo/dom-attr", // domAttr.attr + "dojo/dom-class", // domClass.toggle + "dojo/i18n", // i18n.getLocalization + "dojo/_base/lang", // lang.hitch lang.trim + "./StackController", + "../Menu", + "../MenuItem", + "dojo/text!./templates/_TabButton.html", + "dojo/i18n!../nls/common" +], function(declare, dom, domAttr, domClass, i18n, lang, StackController, Menu, MenuItem, template){ + +/*===== + var StackController = dijit.layout.StackController; + var Menu = dijit.Menu; + var MenuItem = dijit.MenuItem; +=====*/ + + // module: + // dijit/layout/TabController + // summary: + // Set of tabs (the things with titles and a close button, that you click to show a tab panel). + // Used internally by `dijit.layout.TabContainer`. + + var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, { + // summary: + // A tab (the thing you click to select a pane). + // description: + // Contains the title of the pane, and optionally a close-button to destroy the pane. + // This is an internal widget and should not be instantiated directly. + // tags: + // private + + // baseClass: String + // The CSS class applied to the domNode. + baseClass: "dijitTab", + + // Apply dijitTabCloseButtonHover when close button is hovered + cssStateNodes: { + closeNode: "dijitTabCloseButton" + }, + + templateString: template, + + // Override _FormWidget.scrollOnFocus. + // Don't scroll the whole tab container into view when the button is focused. + scrollOnFocus: false, + + buildRendering: function(){ + this.inherited(arguments); + + dom.setSelectable(this.containerNode, false); + }, + + startup: function(){ + this.inherited(arguments); + var n = this.domNode; + + // Required to give IE6 a kick, as it initially hides the + // tabs until they are focused on. + setTimeout(function(){ + n.className = n.className; + }, 1); + }, + + _setCloseButtonAttr: function(/*Boolean*/ disp){ + // summary: + // Hide/show close button + this._set("closeButton", disp); + domClass.toggle(this.innerDiv, "dijitClosable", disp); + this.closeNode.style.display = disp ? "" : "none"; + if(disp){ + var _nlsResources = i18n.getLocalization("dijit", "common"); + if(this.closeNode){ + domAttr.set(this.closeNode,"title", _nlsResources.itemClose); + } + // add context menu onto title button + this._closeMenu = new Menu({ + id: this.id+"_Menu", + dir: this.dir, + lang: this.lang, + textDir: this.textDir, + targetNodeIds: [this.domNode] + }); + + this._closeMenu.addChild(new MenuItem({ + label: _nlsResources.itemClose, + dir: this.dir, + lang: this.lang, + textDir: this.textDir, + onClick: lang.hitch(this, "onClickCloseButton") + })); + }else{ + if(this._closeMenu){ + this._closeMenu.destroyRecursive(); + delete this._closeMenu; + } + } + }, + _setLabelAttr: function(/*String*/ content){ + // summary: + // Hook for set('label', ...) to work. + // description: + // takes an HTML string. + // Inherited ToggleButton implementation will Set the label (text) of the button; + // Need to set the alt attribute of icon on tab buttons if no label displayed + this.inherited(arguments); + if(!this.showLabel && !this.params.title){ + this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || ''); + } + }, + + destroy: function(){ + if(this._closeMenu){ + this._closeMenu.destroyRecursive(); + delete this._closeMenu; + } + this.inherited(arguments); + } + }); + + var TabController = declare("dijit.layout.TabController", StackController, { + // summary: + // Set of tabs (the things with titles and a close button, that you click to show a tab panel). + // Used internally by `dijit.layout.TabContainer`. + // description: + // Lets the user select the currently shown pane in a TabContainer or StackContainer. + // TabController also monitors the TabContainer, and whenever a pane is + // added or deleted updates itself accordingly. + // tags: + // private + + baseClass: "dijitTabController", + + templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>", + + // tabPosition: String + // Defines where tabs go relative to the content. + // "top", "bottom", "left-h", "right-h" + tabPosition: "top", + + // buttonWidget: Constructor + // The tab widget to create to correspond to each page + buttonWidget: TabButton, + + _rectifyRtlTabList: function(){ + // summary: + // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE + + if(0 >= this.tabPosition.indexOf('-h')){ return; } + if(!this.pane2button){ return; } + + var maxWidth = 0; + for(var pane in this.pane2button){ + var ow = this.pane2button[pane].innerDiv.scrollWidth; + maxWidth = Math.max(maxWidth, ow); + } + //unify the length of all the tabs + for(pane in this.pane2button){ + this.pane2button[pane].innerDiv.style.width = maxWidth + 'px'; + } + } + }); + + TabController.TabButton = TabButton; // for monkey patching + + return TabController; +}); diff --git a/lib/dijit/layout/_ContentPaneResizeMixin.js b/lib/dijit/layout/_ContentPaneResizeMixin.js index fe361bee5..e0dd49e96 100644 --- a/lib/dijit/layout/_ContentPaneResizeMixin.js +++ b/lib/dijit/layout/_ContentPaneResizeMixin.js @@ -1,260 +1,2 @@ -/* - 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._ContentPaneResizeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout._ContentPaneResizeMixin"] = true; -dojo.provide("dijit.layout._ContentPaneResizeMixin"); -dojo.require("dijit._Contained"); -dojo.require("dijit.layout._LayoutWidget"); - - -dojo.declare("dijit.layout._ContentPaneResizeMixin", null, { - // summary: - // Resize() functionality of ContentPane. If there's a single layout widget - // child then it will call resize() with the same dimensions as the ContentPane. - // Otherwise just calls resize on each child. - // - // Also implements basic startup() functionality, where starting the parent - // will start the children - - // doLayout: Boolean - // - false - don't adjust size of children - // - true - if there is a single visible child widget, set it's size to - // however big the ContentPane is - doLayout: true, - - // isContainer: [protected] Boolean - // Indicates that this widget acts as a "parent" to the descendant widgets. - // When the parent is started it will call startup() on the child widgets. - // See also `isLayoutContainer`. - isContainer: true, - - // isLayoutContainer: [protected] Boolean - // Indicates that this widget will call resize() on it's child widgets - // when they become visible. - isLayoutContainer: true, - - _startChildren: function(){ - // summary: - // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects - - // This starts all the widgets - dojo.forEach(this.getChildren(), function(child){ - child.startup(); - child._started = true; - }); - }, - - startup: function(){ - // summary: - // See `dijit.layout._LayoutWidget.startup` for description. - // Although ContentPane doesn't extend _LayoutWidget, it does implement - // the same API. - - if(this._started){ return; } - - var parent = dijit._Contained.prototype.getParent.call(this); - this._childOfLayoutWidget = parent && parent.isLayoutContainer; - - // I need to call resize() on my child/children (when I become visible), unless - // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then. - this._needLayout = !this._childOfLayoutWidget; - - this.inherited(arguments); - - this._startChildren(); - - if(this._isShown()){ - this._onShow(); - } - - if(!this._childOfLayoutWidget){ - // If my parent isn't a layout container, since my style *may be* width=height=100% - // or something similar (either set directly or via a CSS class), - // monitor when my size changes so that I can re-layout. - // For browsers where I can't directly monitor when my size changes, - // monitor when the viewport changes size, which *may* indicate a size change for me. - this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){ - // Using function(){} closure to ensure no arguments to resize. - this._needLayout = !this._childOfLayoutWidget; - this.resize(); - }); - } - }, - - _checkIfSingleChild: function(){ - // summary: - // Test if we have exactly one visible widget as a child, - // and if so assume that we are a container for that widget, - // and should propagate startup() and resize() calls to it. - // Skips over things like data stores since they aren't visible. - - var childNodes = dojo.query("> *", this.containerNode).filter(function(node){ - return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc.. - }), - childWidgetNodes = childNodes.filter(function(node){ - return dojo.hasAttr(node, "data-dojo-type") || dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId"); - }), - candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){ - return widget && widget.domNode && widget.resize; - }); - - if( - // all child nodes are widgets - childNodes.length == childWidgetNodes.length && - - // all but one are invisible (like dojo.data) - candidateWidgets.length == 1 - ){ - this._singleChild = candidateWidgets[0]; - }else{ - delete this._singleChild; - } - - // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449) - dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild); - }, - - resize: function(changeSize, resultSize){ - // summary: - // See `dijit.layout._LayoutWidget.resize` for description. - // Although ContentPane doesn't extend _LayoutWidget, it does implement - // the same API. - - // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is - // never called, so resize() is our trigger to do the initial href download (see [20099]). - // However, don't load href for closed TitlePanes. - if(!this._wasShown && this.open !== false){ - this._onShow(); - } - - this._resizeCalled = true; - - this._scheduleLayout(changeSize, resultSize); - }, - - _scheduleLayout: function(changeSize, resultSize){ - // summary: - // Resize myself, and call resize() on each of my child layout widgets, either now - // (if I'm currently visible) or when I become visible - if(this._isShown()){ - this._layout(changeSize, resultSize); - }else{ - this._needLayout = true; - this._changeSize = changeSize; - this._resultSize = resultSize; - } - }, - - _layout: function(changeSize, resultSize){ - // summary: - // Resize myself according to optional changeSize/resultSize parameters, like a layout widget. - // Also, since I am a Container widget, each of my children expects me to - // call resize() or layout() on them. - // - // Should be called on initialization and also whenever we get new content - // (from an href, or from set('content', ...))... but deferred until - // the ContentPane is visible - - // Set margin box size, unless it wasn't specified, in which case use current size. - if(changeSize){ - dojo.marginBox(this.domNode, changeSize); - } - - // Compute content box size of containerNode in case we [later] need to size our single child. - var cn = this.containerNode; - if(cn === this.domNode){ - // If changeSize or resultSize was passed to this method and this.containerNode == - // this.domNode then we can compute the content-box size without querying the node, - // which is more reliable (similar to LayoutWidget.resize) (see for example #9449). - var mb = resultSize || {}; - dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize - if(!("h" in mb) || !("w" in mb)){ - mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values - } - this._contentBox = dijit.layout.marginBox2contentBox(cn, mb); - }else{ - this._contentBox = dojo.contentBox(cn); - } - - this._layoutChildren(); - - delete this._needLayout; - }, - - _layoutChildren: function(){ - // Call _checkIfSingleChild() again in case app has manually mucked w/the content - // of the ContentPane (rather than changing it through the set("content", ...) API. - if(this.doLayout){ - this._checkIfSingleChild(); - } - - if(this._singleChild && this._singleChild.resize){ - var cb = this._contentBox || dojo.contentBox(this.containerNode); - - // note: if widget has padding this._contentBox will have l and t set, - // but don't pass them to resize() or it will doubly-offset the child - this._singleChild.resize({w: cb.w, h: cb.h}); - }else{ - // All my child widgets are independently sized (rather than matching my size), - // but I still need to call resize() on each child to make it layout. - dojo.forEach(this.getChildren(), function(widget){ - if(widget.resize){ - widget.resize(); - } - }); - } - }, - - _isShown: function(){ - // summary: - // Returns true if the content is currently shown. - // description: - // If I am a child of a layout widget then it actually returns true if I've ever been visible, - // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget - // tree every call, and at least solves the performance problem on page load by deferring loading - // hidden ContentPanes until they are first shown - - if(this._childOfLayoutWidget){ - // If we are TitlePane, etc - we return that only *IF* we've been resized - if(this._resizeCalled && "open" in this){ - return this.open; - } - return this._resizeCalled; - }else if("open" in this){ - return this.open; // for TitlePane, etc. - }else{ - var node = this.domNode, parent = this.domNode.parentNode; - return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden") && - parent && parent.style && (parent.style.display != 'none'); - } - }, - - _onShow: function(){ - // summary: - // Called when the ContentPane is made visible - // description: - // For a plain ContentPane, this is called on initialization, from startup(). - // If the ContentPane is a hidden pane of a TabContainer etc., then it's - // called whenever the pane is made visible. - // - // Does layout/resize of child widget(s) - - if(this._needLayout){ - // If a layout has been scheduled for when we become visible, do it now - this._layout(this._changeSize, this._resultSize); - } - - this.inherited(arguments); - - // Need to keep track of whether ContentPane has been shown (which is different than - // whether or not it's currently visible). - this._wasShown = true; - } -}); - -} +//>>built +define("dijit/layout/_ContentPaneResizeMixin",["dojo/_base/array","dojo/_base/declare","dojo/dom-attr","dojo/dom-class","dojo/dom-geometry","dojo/_base/lang","dojo/query","dojo/_base/sniff","dojo/_base/window","../registry","./utils","../_Contained"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c){return _2("dijit.layout._ContentPaneResizeMixin",null,{doLayout:true,isLayoutContainer:true,startup:function(){if(this._started){return;}var _d=this.getParent();this._childOfLayoutWidget=_d&&_d.isLayoutContainer;this._needLayout=!this._childOfLayoutWidget;this.inherited(arguments);if(this._isShown()){this._onShow();}if(!this._childOfLayoutWidget){this.connect(_8("ie")?this.domNode:_9.global,"onresize",function(){this._needLayout=!this._childOfLayoutWidget;this.resize();});}},_checkIfSingleChild:function(){var _e=_7("> *",this.containerNode).filter(function(_f){return _f.tagName!=="SCRIPT";}),_10=_e.filter(function(_11){return _3.has(_11,"data-dojo-type")||_3.has(_11,"dojoType")||_3.has(_11,"widgetId");}),_12=_1.filter(_10.map(_a.byNode),function(_13){return _13&&_13.domNode&&_13.resize;});if(_e.length==_10.length&&_12.length==1){this._singleChild=_12[0];}else{delete this._singleChild;}_4.toggle(this.containerNode,this.baseClass+"SingleChild",!!this._singleChild);},resize:function(_14,_15){if(!this._wasShown&&this.open!==false){this._onShow();}this._resizeCalled=true;this._scheduleLayout(_14,_15);},_scheduleLayout:function(_16,_17){if(this._isShown()){this._layout(_16,_17);}else{this._needLayout=true;this._changeSize=_16;this._resultSize=_17;}},_layout:function(_18,_19){if(_18){_5.setMarginBox(this.domNode,_18);}var cn=this.containerNode;if(cn===this.domNode){var mb=_19||{};_6.mixin(mb,_18||{});if(!("h" in mb)||!("w" in mb)){mb=_6.mixin(_5.getMarginBox(cn),mb);}this._contentBox=_b.marginBox2contentBox(cn,mb);}else{this._contentBox=_5.getContentBox(cn);}this._layoutChildren();delete this._needLayout;},_layoutChildren:function(){if(this.doLayout){this._checkIfSingleChild();}if(this._singleChild&&this._singleChild.resize){var cb=this._contentBox||_5.getContentBox(this.containerNode);this._singleChild.resize({w:cb.w,h:cb.h});}else{_1.forEach(this.getChildren(),function(_1a){if(_1a.resize){_1a.resize();}});}},_isShown:function(){if(this._childOfLayoutWidget){if(this._resizeCalled&&"open" in this){return this.open;}return this._resizeCalled;}else{if("open" in this){return this.open;}else{var _1b=this.domNode,_1c=this.domNode.parentNode;return (_1b.style.display!="none")&&(_1b.style.visibility!="hidden")&&!_4.contains(_1b,"dijitHidden")&&_1c&&_1c.style&&(_1c.style.display!="none");}}},_onShow:function(){if(this._needLayout){this._layout(this._changeSize,this._resultSize);}this.inherited(arguments);this._wasShown=true;}});});
\ No newline at end of file diff --git a/lib/dijit/layout/_ContentPaneResizeMixin.js.uncompressed.js b/lib/dijit/layout/_ContentPaneResizeMixin.js.uncompressed.js new file mode 100644 index 000000000..fbeb3c6eb --- /dev/null +++ b/lib/dijit/layout/_ContentPaneResizeMixin.js.uncompressed.js @@ -0,0 +1,255 @@ +define("dijit/layout/_ContentPaneResizeMixin", [ + "dojo/_base/array", // array.filter array.forEach + "dojo/_base/declare", // declare + "dojo/dom-attr", // domAttr.has + "dojo/dom-class", // domClass.contains domClass.toggle + "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox + "dojo/_base/lang", // lang.mixin + "dojo/query", // query + "dojo/_base/sniff", // has("ie") + "dojo/_base/window", // win.global + "../registry", // registry.byId + "./utils", // marginBox2contextBox + "../_Contained" +], function(array, declare, domAttr, domClass, domGeometry, lang, query, has, win, + registry, layoutUtils, _Contained){ + +/*===== +var _Contained = dijit._Contained; +=====*/ + +// module: +// dijit/layout/_ContentPaneResizeMixin +// summary: +// Resize() functionality of ContentPane. If there's a single layout widget +// child then it will call resize() with the same dimensions as the ContentPane. +// Otherwise just calls resize on each child. + + +return declare("dijit.layout._ContentPaneResizeMixin", null, { + // summary: + // Resize() functionality of ContentPane. If there's a single layout widget + // child then it will call resize() with the same dimensions as the ContentPane. + // Otherwise just calls resize on each child. + // + // Also implements basic startup() functionality, where starting the parent + // will start the children + + // doLayout: Boolean + // - false - don't adjust size of children + // - true - if there is a single visible child widget, set it's size to + // however big the ContentPane is + doLayout: true, + + // isLayoutContainer: [protected] Boolean + // Indicates that this widget will call resize() on it's child widgets + // when they become visible. + isLayoutContainer: true, + + startup: function(){ + // summary: + // See `dijit.layout._LayoutWidget.startup` for description. + // Although ContentPane doesn't extend _LayoutWidget, it does implement + // the same API. + + if(this._started){ return; } + + var parent = this.getParent(); + this._childOfLayoutWidget = parent && parent.isLayoutContainer; + + // I need to call resize() on my child/children (when I become visible), unless + // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then. + this._needLayout = !this._childOfLayoutWidget; + + this.inherited(arguments); + + if(this._isShown()){ + this._onShow(); + } + + if(!this._childOfLayoutWidget){ + // If my parent isn't a layout container, since my style *may be* width=height=100% + // or something similar (either set directly or via a CSS class), + // monitor when my size changes so that I can re-layout. + // For browsers where I can't directly monitor when my size changes, + // monitor when the viewport changes size, which *may* indicate a size change for me. + this.connect(has("ie") ? this.domNode : win.global, 'onresize', function(){ + // Using function(){} closure to ensure no arguments to resize. + this._needLayout = !this._childOfLayoutWidget; + this.resize(); + }); + } + }, + + _checkIfSingleChild: function(){ + // summary: + // Test if we have exactly one visible widget as a child, + // and if so assume that we are a container for that widget, + // and should propagate startup() and resize() calls to it. + // Skips over things like data stores since they aren't visible. + + var childNodes = query("> *", this.containerNode).filter(function(node){ + return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc.. + }), + childWidgetNodes = childNodes.filter(function(node){ + return domAttr.has(node, "data-dojo-type") || domAttr.has(node, "dojoType") || domAttr.has(node, "widgetId"); + }), + candidateWidgets = array.filter(childWidgetNodes.map(registry.byNode), function(widget){ + return widget && widget.domNode && widget.resize; + }); + + if( + // all child nodes are widgets + childNodes.length == childWidgetNodes.length && + + // all but one are invisible (like dojo.data) + candidateWidgets.length == 1 + ){ + this._singleChild = candidateWidgets[0]; + }else{ + delete this._singleChild; + } + + // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449) + domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild); + }, + + resize: function(changeSize, resultSize){ + // summary: + // See `dijit.layout._LayoutWidget.resize` for description. + // Although ContentPane doesn't extend _LayoutWidget, it does implement + // the same API. + + // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is + // never called, so resize() is our trigger to do the initial href download (see [20099]). + // However, don't load href for closed TitlePanes. + if(!this._wasShown && this.open !== false){ + this._onShow(); + } + + this._resizeCalled = true; + + this._scheduleLayout(changeSize, resultSize); + }, + + _scheduleLayout: function(changeSize, resultSize){ + // summary: + // Resize myself, and call resize() on each of my child layout widgets, either now + // (if I'm currently visible) or when I become visible + if(this._isShown()){ + this._layout(changeSize, resultSize); + }else{ + this._needLayout = true; + this._changeSize = changeSize; + this._resultSize = resultSize; + } + }, + + _layout: function(changeSize, resultSize){ + // summary: + // Resize myself according to optional changeSize/resultSize parameters, like a layout widget. + // Also, since I am a Container widget, each of my children expects me to + // call resize() or layout() on them. + // + // Should be called on initialization and also whenever we get new content + // (from an href, or from set('content', ...))... but deferred until + // the ContentPane is visible + + // Set margin box size, unless it wasn't specified, in which case use current size. + if(changeSize){ + domGeometry.setMarginBox(this.domNode, changeSize); + } + + // Compute content box size of containerNode in case we [later] need to size our single child. + var cn = this.containerNode; + if(cn === this.domNode){ + // If changeSize or resultSize was passed to this method and this.containerNode == + // this.domNode then we can compute the content-box size without querying the node, + // which is more reliable (similar to LayoutWidget.resize) (see for example #9449). + var mb = resultSize || {}; + lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize + if(!("h" in mb) || !("w" in mb)){ + mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values + } + this._contentBox = layoutUtils.marginBox2contentBox(cn, mb); + }else{ + this._contentBox = domGeometry.getContentBox(cn); + } + + this._layoutChildren(); + + delete this._needLayout; + }, + + _layoutChildren: function(){ + // Call _checkIfSingleChild() again in case app has manually mucked w/the content + // of the ContentPane (rather than changing it through the set("content", ...) API. + if(this.doLayout){ + this._checkIfSingleChild(); + } + + if(this._singleChild && this._singleChild.resize){ + var cb = this._contentBox || domGeometry.getContentBox(this.containerNode); + + // note: if widget has padding this._contentBox will have l and t set, + // but don't pass them to resize() or it will doubly-offset the child + this._singleChild.resize({w: cb.w, h: cb.h}); + }else{ + // All my child widgets are independently sized (rather than matching my size), + // but I still need to call resize() on each child to make it layout. + array.forEach(this.getChildren(), function(widget){ + if(widget.resize){ + widget.resize(); + } + }); + } + }, + + _isShown: function(){ + // summary: + // Returns true if the content is currently shown. + // description: + // If I am a child of a layout widget then it actually returns true if I've ever been visible, + // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget + // tree every call, and at least solves the performance problem on page load by deferring loading + // hidden ContentPanes until they are first shown + + if(this._childOfLayoutWidget){ + // If we are TitlePane, etc - we return that only *IF* we've been resized + if(this._resizeCalled && "open" in this){ + return this.open; + } + return this._resizeCalled; + }else if("open" in this){ + return this.open; // for TitlePane, etc. + }else{ + var node = this.domNode, parent = this.domNode.parentNode; + return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") && + parent && parent.style && (parent.style.display != 'none'); + } + }, + + _onShow: function(){ + // summary: + // Called when the ContentPane is made visible + // description: + // For a plain ContentPane, this is called on initialization, from startup(). + // If the ContentPane is a hidden pane of a TabContainer etc., then it's + // called whenever the pane is made visible. + // + // Does layout/resize of child widget(s) + + if(this._needLayout){ + // If a layout has been scheduled for when we become visible, do it now + this._layout(this._changeSize, this._resultSize); + } + + this.inherited(arguments); + + // Need to keep track of whether ContentPane has been shown (which is different than + // whether or not it's currently visible). + this._wasShown = true; + } +}); + +}); diff --git a/lib/dijit/layout/_LayoutWidget.js b/lib/dijit/layout/_LayoutWidget.js index cbbf71009..7ac78de47 100644 --- a/lib/dijit/layout/_LayoutWidget.js +++ b/lib/dijit/layout/_LayoutWidget.js @@ -1,313 +1,2 @@ -/* - 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._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout._LayoutWidget"] = true; -dojo.provide("dijit.layout._LayoutWidget"); -dojo.require("dijit._Widget"); -dojo.require("dijit._Container"); -dojo.require("dijit._Contained"); - - -dojo.declare("dijit.layout._LayoutWidget", - [dijit._Widget, dijit._Container, dijit._Contained], - { - // summary: - // Base class for a _Container widget which is responsible for laying out its children. - // Widgets which mixin this code must define layout() to manage placement and sizing of the children. - - // baseClass: [protected extension] String - // This class name is applied to the widget's domNode - // and also may be used to generate names for sub nodes, - // for example dijitTabContainer-content. - baseClass: "dijitLayoutContainer", - - // isLayoutContainer: [protected] Boolean - // Indicates that this widget is going to call resize() on its - // children widgets, setting their size, when they become visible. - isLayoutContainer: true, - - buildRendering: function(){ - this.inherited(arguments); - dojo.addClass(this.domNode, "dijitContainer"); - }, - - startup: function(){ - // summary: - // Called after all the widgets have been instantiated and their - // dom nodes have been inserted somewhere under dojo.doc.body. - // - // Widgets should override this method to do any initialization - // dependent on other widgets existing, and then call - // this superclass method to finish things off. - // - // startup() in subclasses shouldn't do anything - // size related because the size of the widget hasn't been set yet. - - if(this._started){ return; } - - // Need to call inherited first - so that child widgets get started - // up correctly - this.inherited(arguments); - - // If I am a not being controlled by a parent layout widget... - var parent = this.getParent && this.getParent() - if(!(parent && parent.isLayoutContainer)){ - // Do recursive sizing and layout of all my descendants - // (passing in no argument to resize means that it has to glean the size itself) - this.resize(); - - // Since my parent isn't a layout container, and my style *may be* width=height=100% - // or something similar (either set directly or via a CSS class), - // monitor when my size changes so that I can re-layout. - // For browsers where I can't directly monitor when my size changes, - // monitor when the viewport changes size, which *may* indicate a size change for me. - this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){ - // Using function(){} closure to ensure no arguments to resize. - this.resize(); - }); - } - }, - - resize: function(changeSize, resultSize){ - // summary: - // Call this to resize a widget, or after its size has changed. - // description: - // Change size mode: - // When changeSize is specified, changes the marginBox of this widget - // and forces it to relayout its contents accordingly. - // changeSize may specify height, width, or both. - // - // If resultSize is specified it indicates the size the widget will - // become after changeSize has been applied. - // - // Notification mode: - // When changeSize is null, indicates that the caller has already changed - // the size of the widget, or perhaps it changed because the browser - // window was resized. Tells widget to relayout its contents accordingly. - // - // If resultSize is also specified it indicates the size the widget has - // become. - // - // In either mode, this method also: - // 1. Sets this._borderBox and this._contentBox to the new size of - // the widget. Queries the current domNode size if necessary. - // 2. Calls layout() to resize contents (and maybe adjust child widgets). - // - // changeSize: Object? - // Sets the widget to this margin-box size and position. - // May include any/all of the following properties: - // | {w: int, h: int, l: int, t: int} - // - // resultSize: Object? - // The margin-box size of this widget after applying changeSize (if - // changeSize is specified). If caller knows this size and - // passes it in, we don't need to query the browser to get the size. - // | {w: int, h: int} - - var node = this.domNode; - - // set margin box size, unless it wasn't specified, in which case use current size - if(changeSize){ - dojo.marginBox(node, changeSize); - - // set offset of the node - if(changeSize.t){ node.style.top = changeSize.t + "px"; } - if(changeSize.l){ node.style.left = changeSize.l + "px"; } - } - - // If either height or width wasn't specified by the user, then query node for it. - // But note that setting the margin box and then immediately querying dimensions may return - // inaccurate results, so try not to depend on it. - var mb = resultSize || {}; - dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize - if( !("h" in mb) || !("w" in mb) ){ - mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values - } - - // Compute and save the size of my border box and content box - // (w/out calling dojo.contentBox() since that may fail if size was recently set) - var cs = dojo.getComputedStyle(node); - var me = dojo._getMarginExtents(node, cs); - var be = dojo._getBorderExtents(node, cs); - var bb = (this._borderBox = { - w: mb.w - (me.w + be.w), - h: mb.h - (me.h + be.h) - }); - var pe = dojo._getPadExtents(node, cs); - this._contentBox = { - l: dojo._toPixelValue(node, cs.paddingLeft), - t: dojo._toPixelValue(node, cs.paddingTop), - w: bb.w - pe.w, - h: bb.h - pe.h - }; - - // Callback for widget to adjust size of its children - this.layout(); - }, - - layout: function(){ - // summary: - // Widgets override this method to size and position their contents/children. - // When this is called this._contentBox is guaranteed to be set (see resize()). - // - // This is called after startup(), and also when the widget's size has been - // changed. - // tags: - // protected extension - }, - - _setupChild: function(/*dijit._Widget*/child){ - // summary: - // Common setup for initial children and children which are added after startup - // tags: - // protected extension - - var cls = this.baseClass + "-child " - + (child.baseClass ? this.baseClass + "-" + child.baseClass : ""); - dojo.addClass(child.domNode, cls); - }, - - addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ - // Overrides _Container.addChild() to call _setupChild() - this.inherited(arguments); - if(this._started){ - this._setupChild(child); - } - }, - - removeChild: function(/*dijit._Widget*/ child){ - // Overrides _Container.removeChild() to remove class added by _setupChild() - var cls = this.baseClass + "-child" - + (child.baseClass ? - " " + this.baseClass + "-" + child.baseClass : ""); - dojo.removeClass(child.domNode, cls); - - this.inherited(arguments); - } - } -); - -dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ - // summary: - // Given the margin-box size of a node, return its content box size. - // Functions like dojo.contentBox() but is more reliable since it doesn't have - // to wait for the browser to compute sizes. - var cs = dojo.getComputedStyle(node); - var me = dojo._getMarginExtents(node, cs); - var pb = dojo._getPadBorderExtents(node, cs); - return { - l: dojo._toPixelValue(node, cs.paddingLeft), - t: dojo._toPixelValue(node, cs.paddingTop), - w: mb.w - (me.w + pb.w), - h: mb.h - (me.h + pb.h) - }; -}; - -(function(){ - var capitalize = function(word){ - return word.substring(0,1).toUpperCase() + word.substring(1); - }; - - var size = function(widget, dim){ - // size the child - var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim); - - // record child's size - if(newSize){ - // if the child returned it's new size then use that - dojo.mixin(widget, newSize); - }else{ - // otherwise, call marginBox(), but favor our own numbers when we have them. - // the browser lies sometimes - dojo.mixin(widget, dojo.marginBox(widget.domNode)); - dojo.mixin(widget, dim); - } - }; - - dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children, - /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){ - // summary - // Layout a bunch of child dom nodes within a parent dom node - // container: - // parent node - // dim: - // {l, t, w, h} object specifying dimensions of container into which to place children - // children: - // an array of Widgets or at least objects containing: - // * domNode: pointer to DOM node to position - // * region or layoutAlign: position to place DOM node - // * resize(): (optional) method to set size of node - // * id: (optional) Id of widgets, referenced from resize object, below. - // changedRegionId: - // If specified, the slider for the region with the specified id has been dragged, and thus - // the region's height or width should be adjusted according to changedRegionSize - // changedRegionSize: - // See changedRegionId. - - // copy dim because we are going to modify it - dim = dojo.mixin({}, dim); - - dojo.addClass(container, "dijitLayoutContainer"); - - // Move "client" elements to the end of the array for layout. a11y dictates that the author - // needs to be able to put them in the document in tab-order, but this algorithm requires that - // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think. - children = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; }) - .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; })); - - // set positions/sizes - dojo.forEach(children, function(child){ - var elm = child.domNode, - pos = (child.region || child.layoutAlign); - - // set elem to upper left corner of unused space; may move it later - var elmStyle = elm.style; - elmStyle.left = dim.l+"px"; - elmStyle.top = dim.t+"px"; - elmStyle.position = "absolute"; - - dojo.addClass(elm, "dijitAlign" + capitalize(pos)); - - // Size adjustments to make to this child widget - var sizeSetting = {}; - - // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align - // panes and width adjustment for left/right align panes. - if(changedRegionId && changedRegionId == child.id){ - sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize; - } - - // set size && adjust record of remaining space. - // note that setting the width of a <div> may affect its height. - if(pos == "top" || pos == "bottom"){ - sizeSetting.w = dim.w; - size(child, sizeSetting); - dim.h -= child.h; - if(pos == "top"){ - dim.t += child.h; - }else{ - elmStyle.top = dim.t + dim.h + "px"; - } - }else if(pos == "left" || pos == "right"){ - sizeSetting.h = dim.h; - size(child, sizeSetting); - dim.w -= child.w; - if(pos == "left"){ - dim.l += child.w; - }else{ - elmStyle.left = dim.l + dim.w + "px"; - } - }else if(pos == "client" || pos == "center"){ - size(child, dim); - } - }); - }; - -})(); - -} +//>>built +define("dijit/layout/_LayoutWidget",["dojo/_base/lang","../_Widget","../_Container","../_Contained","dojo/_base/declare","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/_base/sniff","dojo/_base/window"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a){return _5("dijit.layout._LayoutWidget",[_2,_3,_4],{baseClass:"dijitLayoutContainer",isLayoutContainer:true,buildRendering:function(){this.inherited(arguments);_6.add(this.domNode,"dijitContainer");},startup:function(){if(this._started){return;}this.inherited(arguments);var _b=this.getParent&&this.getParent();if(!(_b&&_b.isLayoutContainer)){this.resize();this.connect(_a.global,"onresize",function(){this.resize();});}},resize:function(_c,_d){var _e=this.domNode;if(_c){_7.setMarginBox(_e,_c);}var mb=_d||{};_1.mixin(mb,_c||{});if(!("h" in mb)||!("w" in mb)){mb=_1.mixin(_7.getMarginBox(_e),mb);}var cs=_8.getComputedStyle(_e);var me=_7.getMarginExtents(_e,cs);var be=_7.getBorderExtents(_e,cs);var bb=(this._borderBox={w:mb.w-(me.w+be.w),h:mb.h-(me.h+be.h)});var pe=_7.getPadExtents(_e,cs);this._contentBox={l:_8.toPixelValue(_e,cs.paddingLeft),t:_8.toPixelValue(_e,cs.paddingTop),w:bb.w-pe.w,h:bb.h-pe.h};this.layout();},layout:function(){},_setupChild:function(_f){var cls=this.baseClass+"-child "+(_f.baseClass?this.baseClass+"-"+_f.baseClass:"");_6.add(_f.domNode,cls);},addChild:function(_10,_11){this.inherited(arguments);if(this._started){this._setupChild(_10);}},removeChild:function(_12){var cls=this.baseClass+"-child"+(_12.baseClass?" "+this.baseClass+"-"+_12.baseClass:"");_6.remove(_12.domNode,cls);this.inherited(arguments);}});});
\ No newline at end of file diff --git a/lib/dijit/layout/_LayoutWidget.js.uncompressed.js b/lib/dijit/layout/_LayoutWidget.js.uncompressed.js new file mode 100644 index 000000000..1111d2539 --- /dev/null +++ b/lib/dijit/layout/_LayoutWidget.js.uncompressed.js @@ -0,0 +1,197 @@ +define("dijit/layout/_LayoutWidget", [ + "dojo/_base/lang", // lang.mixin + "../_Widget", + "../_Container", + "../_Contained", + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add domClass.remove + "dojo/dom-geometry", // domGeometry.marginBox + "dojo/dom-style", // domStyle.getComputedStyle + "dojo/_base/sniff", // has("ie") + "dojo/_base/window" // win.global +], function(lang, _Widget, _Container, _Contained, + declare, domClass, domGeometry, domStyle, has, win){ + +/*===== + var _Widget = dijit._Widget; + var _Container = dijit._Container; + var _Contained = dijit._Contained; +=====*/ + + // module: + // dijit/layout/_LayoutWidget + // summary: + // _LayoutWidget Base class for a _Container widget which is responsible for laying out its children. + // Widgets which mixin this code must define layout() to manage placement and sizing of the children. + + + return declare("dijit.layout._LayoutWidget", [_Widget, _Container, _Contained], { + // summary: + // Base class for a _Container widget which is responsible for laying out its children. + // Widgets which mixin this code must define layout() to manage placement and sizing of the children. + + // baseClass: [protected extension] String + // This class name is applied to the widget's domNode + // and also may be used to generate names for sub nodes, + // for example dijitTabContainer-content. + baseClass: "dijitLayoutContainer", + + // isLayoutContainer: [protected] Boolean + // Indicates that this widget is going to call resize() on its + // children widgets, setting their size, when they become visible. + isLayoutContainer: true, + + buildRendering: function(){ + this.inherited(arguments); + domClass.add(this.domNode, "dijitContainer"); + }, + + startup: function(){ + // summary: + // Called after all the widgets have been instantiated and their + // dom nodes have been inserted somewhere under win.doc.body. + // + // Widgets should override this method to do any initialization + // dependent on other widgets existing, and then call + // this superclass method to finish things off. + // + // startup() in subclasses shouldn't do anything + // size related because the size of the widget hasn't been set yet. + + if(this._started){ return; } + + // Need to call inherited first - so that child widgets get started + // up correctly + this.inherited(arguments); + + // If I am a not being controlled by a parent layout widget... + var parent = this.getParent && this.getParent(); + if(!(parent && parent.isLayoutContainer)){ + // Do recursive sizing and layout of all my descendants + // (passing in no argument to resize means that it has to glean the size itself) + this.resize(); + + // Since my parent isn't a layout container, and my style *may be* width=height=100% + // or something similar (either set directly or via a CSS class), + // monitor when viewport size changes so that I can re-layout. + this.connect(win.global, 'onresize', function(){ + // Using function(){} closure to ensure no arguments passed to resize(). + this.resize(); + }); + } + }, + + resize: function(changeSize, resultSize){ + // summary: + // Call this to resize a widget, or after its size has changed. + // description: + // Change size mode: + // When changeSize is specified, changes the marginBox of this widget + // and forces it to relayout its contents accordingly. + // changeSize may specify height, width, or both. + // + // If resultSize is specified it indicates the size the widget will + // become after changeSize has been applied. + // + // Notification mode: + // When changeSize is null, indicates that the caller has already changed + // the size of the widget, or perhaps it changed because the browser + // window was resized. Tells widget to relayout its contents accordingly. + // + // If resultSize is also specified it indicates the size the widget has + // become. + // + // In either mode, this method also: + // 1. Sets this._borderBox and this._contentBox to the new size of + // the widget. Queries the current domNode size if necessary. + // 2. Calls layout() to resize contents (and maybe adjust child widgets). + // + // changeSize: Object? + // Sets the widget to this margin-box size and position. + // May include any/all of the following properties: + // | {w: int, h: int, l: int, t: int} + // + // resultSize: Object? + // The margin-box size of this widget after applying changeSize (if + // changeSize is specified). If caller knows this size and + // passes it in, we don't need to query the browser to get the size. + // | {w: int, h: int} + + var node = this.domNode; + + // set margin box size, unless it wasn't specified, in which case use current size + if(changeSize){ + domGeometry.setMarginBox(node, changeSize); + } + + // If either height or width wasn't specified by the user, then query node for it. + // But note that setting the margin box and then immediately querying dimensions may return + // inaccurate results, so try not to depend on it. + var mb = resultSize || {}; + lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize + if( !("h" in mb) || !("w" in mb) ){ + mb = lang.mixin(domGeometry.getMarginBox(node), mb); // just use domGeometry.marginBox() to fill in missing values + } + + // Compute and save the size of my border box and content box + // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set) + var cs = domStyle.getComputedStyle(node); + var me = domGeometry.getMarginExtents(node, cs); + var be = domGeometry.getBorderExtents(node, cs); + var bb = (this._borderBox = { + w: mb.w - (me.w + be.w), + h: mb.h - (me.h + be.h) + }); + var pe = domGeometry.getPadExtents(node, cs); + this._contentBox = { + l: domStyle.toPixelValue(node, cs.paddingLeft), + t: domStyle.toPixelValue(node, cs.paddingTop), + w: bb.w - pe.w, + h: bb.h - pe.h + }; + + // Callback for widget to adjust size of its children + this.layout(); + }, + + layout: function(){ + // summary: + // Widgets override this method to size and position their contents/children. + // When this is called this._contentBox is guaranteed to be set (see resize()). + // + // This is called after startup(), and also when the widget's size has been + // changed. + // tags: + // protected extension + }, + + _setupChild: function(/*dijit._Widget*/child){ + // summary: + // Common setup for initial children and children which are added after startup + // tags: + // protected extension + + var cls = this.baseClass + "-child " + + (child.baseClass ? this.baseClass + "-" + child.baseClass : ""); + domClass.add(child.domNode, cls); + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + // Overrides _Container.addChild() to call _setupChild() + this.inherited(arguments); + if(this._started){ + this._setupChild(child); + } + }, + + removeChild: function(/*dijit._Widget*/ child){ + // Overrides _Container.removeChild() to remove class added by _setupChild() + var cls = this.baseClass + "-child" + + (child.baseClass ? + " " + this.baseClass + "-" + child.baseClass : ""); + domClass.remove(child.domNode, cls); + + this.inherited(arguments); + } + }); +}); diff --git a/lib/dijit/layout/_TabContainerBase.js b/lib/dijit/layout/_TabContainerBase.js index 5e07cb653..27e4f86be 100644 --- a/lib/dijit/layout/_TabContainerBase.js +++ b/lib/dijit/layout/_TabContainerBase.js @@ -1,145 +1,2 @@ -/* - 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._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dijit.layout._TabContainerBase"] = true; -dojo.provide("dijit.layout._TabContainerBase"); -dojo.require("dijit.layout.StackContainer"); -dojo.require("dijit._Templated"); - - -dojo.declare("dijit.layout._TabContainerBase", - [dijit.layout.StackContainer, dijit._Templated], - { - // summary: - // Abstract base class for TabContainer. Must define _makeController() to instantiate - // and return the widget that displays the tab labels - // description: - // A TabContainer is a container that has multiple panes, but shows only - // one pane at a time. There are a set of tabs corresponding to each pane, - // where each tab has the name (aka title) of the pane, and optionally a close button. - - // tabPosition: String - // Defines where tabs go relative to tab content. - // "top", "bottom", "left-h", "right-h" - tabPosition: "top", - - baseClass: "dijitTabContainer", - - // tabStrip: [const] Boolean - // Defines whether the tablist gets an extra class for layouting, putting a border/shading - // around the set of tabs. Not supported by claro theme. - tabStrip: false, - - // nested: [const] Boolean - // If true, use styling for a TabContainer nested inside another TabContainer. - // For tundra etc., makes tabs look like links, and hides the outer - // border since the outer TabContainer already has a border. - nested: false, - - templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" dojoAttachPoint=\"tablistNode\"></div>\n\t<div dojoAttachPoint=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n"), - - postMixInProperties: function(){ - // set class name according to tab position, ex: dijitTabContainerTop - this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, ""); - - this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden"); - - this.inherited(arguments); - }, - - buildRendering: function(){ - this.inherited(arguments); - - // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel - this.tablist = this._makeController(this.tablistNode); - - if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); } - - if(this.nested){ - /* workaround IE's lack of support for "a > b" selectors by - * tagging each node in the template. - */ - dojo.addClass(this.domNode, "dijitTabContainerNested"); - dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested"); - dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested"); - dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested"); - }else{ - dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled")); - } - }, - - _setupChild: function(/*dijit._Widget*/ tab){ - // Overrides StackContainer._setupChild(). - dojo.addClass(tab.domNode, "dijitTabPane"); - this.inherited(arguments); - }, - - startup: function(){ - if(this._started){ return; } - - // wire up the tablist and its tabs - this.tablist.startup(); - - this.inherited(arguments); - }, - - layout: function(){ - // Overrides StackContainer.layout(). - // Configure the content pane to take up all the space except for where the tabs are - - if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;} - - var sc = this.selectedChildWidget; - - if(this.doLayout){ - // position and size the titles and the container node - var titleAlign = this.tabPosition.replace(/-h/, ""); - this.tablist.layoutAlign = titleAlign; - var children = [this.tablist, { - domNode: this.tablistSpacer, - layoutAlign: titleAlign - }, { - domNode: this.containerNode, - layoutAlign: "client" - }]; - dijit.layout.layoutChildren(this.domNode, this._contentBox, children); - - // Compute size to make each of my children. - // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above - this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]); - - if(sc && sc.resize){ - sc.resize(this._containerContentBox); - } - }else{ - // just layout the tab controller, so it can position left/right buttons etc. - if(this.tablist.resize){ - //make the tabs zero width so that they don't interfere with width calc, then reset - var s = this.tablist.domNode.style; - s.width="0"; - var width = dojo.contentBox(this.domNode).w; - s.width=""; - this.tablist.resize({w: width}); - } - - // and call resize() on the selected pane just to tell it that it's been made visible - if(sc && sc.resize){ - sc.resize(); - } - } - }, - - destroy: function(){ - if(this.tablist){ - this.tablist.destroy(); - } - this.inherited(arguments); - } -}); - -} +//>>built +require({cache:{"url:dijit/layout/templates/TabContainer.html":"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}});define("dijit/layout/_TabContainerBase",["dojo/text!./templates/TabContainer.html","./StackContainer","./utils","../_TemplatedMixin","dojo/_base/declare","dojo/dom-class","dojo/dom-geometry","dojo/dom-style"],function(_1,_2,_3,_4,_5,_6,_7,_8){return _5("dijit.layout._TabContainerBase",[_2,_4],{tabPosition:"top",baseClass:"dijitTabContainer",tabStrip:false,nested:false,templateString:_1,postMixInProperties:function(){this.baseClass+=this.tabPosition.charAt(0).toUpperCase()+this.tabPosition.substr(1).replace(/-.*/,"");this.srcNodeRef&&_8.set(this.srcNodeRef,"visibility","hidden");this.inherited(arguments);},buildRendering:function(){this.inherited(arguments);this.tablist=this._makeController(this.tablistNode);if(!this.doLayout){_6.add(this.domNode,"dijitTabContainerNoLayout");}if(this.nested){_6.add(this.domNode,"dijitTabContainerNested");_6.add(this.tablist.containerNode,"dijitTabContainerTabListNested");_6.add(this.tablistSpacer,"dijitTabContainerSpacerNested");_6.add(this.containerNode,"dijitTabPaneWrapperNested");}else{_6.add(this.domNode,"tabStrip-"+(this.tabStrip?"enabled":"disabled"));}},_setupChild:function(_9){_6.add(_9.domNode,"dijitTabPane");this.inherited(arguments);},startup:function(){if(this._started){return;}this.tablist.startup();this.inherited(arguments);},layout:function(){if(!this._contentBox||typeof (this._contentBox.l)=="undefined"){return;}var sc=this.selectedChildWidget;if(this.doLayout){var _a=this.tabPosition.replace(/-h/,"");this.tablist.layoutAlign=_a;var _b=[this.tablist,{domNode:this.tablistSpacer,layoutAlign:_a},{domNode:this.containerNode,layoutAlign:"client"}];_3.layoutChildren(this.domNode,this._contentBox,_b);this._containerContentBox=_3.marginBox2contentBox(this.containerNode,_b[2]);if(sc&&sc.resize){sc.resize(this._containerContentBox);}}else{if(this.tablist.resize){var s=this.tablist.domNode.style;s.width="0";var _c=_7.getContentBox(this.domNode).w;s.width="";this.tablist.resize({w:_c});}if(sc&&sc.resize){sc.resize();}}},destroy:function(){if(this.tablist){this.tablist.destroy();}this.inherited(arguments);}});});
\ No newline at end of file diff --git a/lib/dijit/layout/_TabContainerBase.js.uncompressed.js b/lib/dijit/layout/_TabContainerBase.js.uncompressed.js new file mode 100644 index 000000000..5a6090429 --- /dev/null +++ b/lib/dijit/layout/_TabContainerBase.js.uncompressed.js @@ -0,0 +1,155 @@ +require({cache:{ +'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}}); +define("dijit/layout/_TabContainerBase", [ + "dojo/text!./templates/TabContainer.html", + "./StackContainer", + "./utils", // marginBox2contextBox, layoutChildren + "../_TemplatedMixin", + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add + "dojo/dom-geometry", // domGeometry.contentBox + "dojo/dom-style" // domStyle.style +], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){ + + +/*===== + var StackContainer = dijit.layout.StackContainer; + var _TemplatedMixin = dijit._TemplatedMixin; +=====*/ + +// module: +// dijit/layout/_TabContainerBase +// summary: +// Abstract base class for TabContainer. Must define _makeController() to instantiate +// and return the widget that displays the tab labels + + +return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], { + // summary: + // Abstract base class for TabContainer. Must define _makeController() to instantiate + // and return the widget that displays the tab labels + // description: + // A TabContainer is a container that has multiple panes, but shows only + // one pane at a time. There are a set of tabs corresponding to each pane, + // where each tab has the name (aka title) of the pane, and optionally a close button. + + // tabPosition: String + // Defines where tabs go relative to tab content. + // "top", "bottom", "left-h", "right-h" + tabPosition: "top", + + baseClass: "dijitTabContainer", + + // tabStrip: [const] Boolean + // Defines whether the tablist gets an extra class for layouting, putting a border/shading + // around the set of tabs. Not supported by claro theme. + tabStrip: false, + + // nested: [const] Boolean + // If true, use styling for a TabContainer nested inside another TabContainer. + // For tundra etc., makes tabs look like links, and hides the outer + // border since the outer TabContainer already has a border. + nested: false, + + templateString: template, + + postMixInProperties: function(){ + // set class name according to tab position, ex: dijitTabContainerTop + this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, ""); + + this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden"); + + this.inherited(arguments); + }, + + buildRendering: function(){ + this.inherited(arguments); + + // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel + this.tablist = this._makeController(this.tablistNode); + + if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); } + + if(this.nested){ + /* workaround IE's lack of support for "a > b" selectors by + * tagging each node in the template. + */ + domClass.add(this.domNode, "dijitTabContainerNested"); + domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested"); + domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested"); + domClass.add(this.containerNode, "dijitTabPaneWrapperNested"); + }else{ + domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled")); + } + }, + + _setupChild: function(/*dijit._Widget*/ tab){ + // Overrides StackContainer._setupChild(). + domClass.add(tab.domNode, "dijitTabPane"); + this.inherited(arguments); + }, + + startup: function(){ + if(this._started){ return; } + + // wire up the tablist and its tabs + this.tablist.startup(); + + this.inherited(arguments); + }, + + layout: function(){ + // Overrides StackContainer.layout(). + // Configure the content pane to take up all the space except for where the tabs are + + if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;} + + var sc = this.selectedChildWidget; + + if(this.doLayout){ + // position and size the titles and the container node + var titleAlign = this.tabPosition.replace(/-h/, ""); + this.tablist.layoutAlign = titleAlign; + var children = [this.tablist, { + domNode: this.tablistSpacer, + layoutAlign: titleAlign + }, { + domNode: this.containerNode, + layoutAlign: "client" + }]; + layoutUtils.layoutChildren(this.domNode, this._contentBox, children); + + // Compute size to make each of my children. + // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above + this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]); + + if(sc && sc.resize){ + sc.resize(this._containerContentBox); + } + }else{ + // just layout the tab controller, so it can position left/right buttons etc. + if(this.tablist.resize){ + //make the tabs zero width so that they don't interfere with width calc, then reset + var s = this.tablist.domNode.style; + s.width="0"; + var width = domGeometry.getContentBox(this.domNode).w; + s.width=""; + this.tablist.resize({w: width}); + } + + // and call resize() on the selected pane just to tell it that it's been made visible + if(sc && sc.resize){ + sc.resize(); + } + } + }, + + destroy: function(){ + if(this.tablist){ + this.tablist.destroy(); + } + this.inherited(arguments); + } +}); + +}); diff --git a/lib/dijit/layout/templates/AccordionButton.html b/lib/dijit/layout/templates/AccordionButton.html new file mode 100644 index 000000000..d2a9eee40 --- /dev/null +++ b/lib/dijit/layout/templates/AccordionButton.html @@ -0,0 +1,10 @@ +<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role="presentation"> + <div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress' + class='dijitAccordionTitleFocus' role="tab" aria-expanded="false" + ><span class='dijitInline dijitAccordionArrow' role="presentation"></span + ><span class='arrowTextUp' role="presentation">+</span + ><span class='arrowTextDown' role="presentation">-</span + ><img src="${_blankGif}" alt="" class="dijitIcon" data-dojo-attach-point='iconNode' style="vertical-align: middle" role="presentation"/> + <span role="presentation" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span> + </div> +</div> diff --git a/lib/dijit/layout/templates/ScrollingTabController.html b/lib/dijit/layout/templates/ScrollingTabController.html new file mode 100644 index 000000000..505b92fc8 --- /dev/null +++ b/lib/dijit/layout/templates/ScrollingTabController.html @@ -0,0 +1,22 @@ +<div class="dijitTabListContainer-${tabPosition}" style="visibility:hidden"> + <div data-dojo-type="dijit.layout._ScrollingTabControllerMenuButton" + class="tabStripButton-${tabPosition}" + id="${id}_menuBtn" + data-dojo-props="containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon', + dropDownPosition: ['below-alt', 'above-alt']" + data-dojo-attach-point="_menuBtn" showLabel="false" title="">▼</div> + <div data-dojo-type="dijit.layout._ScrollingTabControllerButton" + class="tabStripButton-${tabPosition}" + id="${id}_leftBtn" + data-dojo-props="iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''" + data-dojo-attach-point="_leftBtn" data-dojo-attach-event="onClick: doSlideLeft">◀</div> + <div data-dojo-type="dijit.layout._ScrollingTabControllerButton" + class="tabStripButton-${tabPosition}" + id="${id}_rightBtn" + data-dojo-props="iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''" + data-dojo-attach-point="_rightBtn" data-dojo-attach-event="onClick: doSlideRight">▶</div> + <div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'> + <div role='tablist' data-dojo-attach-event='onkeypress:onkeypress' + data-dojo-attach-point='containerNode' class='nowrapTabStrip'></div> + </div> +</div>
\ No newline at end of file diff --git a/lib/dijit/layout/templates/TabContainer.html b/lib/dijit/layout/templates/TabContainer.html new file mode 100644 index 000000000..4630dbcba --- /dev/null +++ b/lib/dijit/layout/templates/TabContainer.html @@ -0,0 +1,5 @@ +<div class="dijitTabContainer"> + <div class="dijitTabListWrapper" data-dojo-attach-point="tablistNode"></div> + <div data-dojo-attach-point="tablistSpacer" class="dijitTabSpacer ${baseClass}-spacer"></div> + <div class="dijitTabPaneWrapper ${baseClass}-container" data-dojo-attach-point="containerNode"></div> +</div> diff --git a/lib/dijit/layout/templates/_ScrollingTabControllerButton.html b/lib/dijit/layout/templates/_ScrollingTabControllerButton.html new file mode 100644 index 000000000..c7ef933a1 --- /dev/null +++ b/lib/dijit/layout/templates/_ScrollingTabControllerButton.html @@ -0,0 +1,8 @@ +<div data-dojo-attach-event="onclick:_onClick"> + <div role="presentation" class="dijitTabInnerDiv" data-dojo-attach-point="innerDiv,focusNode"> + <div role="presentation" class="dijitTabContent dijitButtonContents" data-dojo-attach-point="tabContent"> + <img role="presentation" alt="" src="${_blankGif}" class="dijitTabStripIcon" data-dojo-attach-point="iconNode"/> + <span data-dojo-attach-point="containerNode,titleNode" class="dijitButtonText"></span> + </div> + </div> +</div>
\ No newline at end of file diff --git a/lib/dijit/layout/templates/_TabButton.html b/lib/dijit/layout/templates/_TabButton.html new file mode 100644 index 000000000..7d6570e73 --- /dev/null +++ b/lib/dijit/layout/templates/_TabButton.html @@ -0,0 +1,14 @@ +<div role="presentation" data-dojo-attach-point="titleNode" data-dojo-attach-event='onclick:onClick'> + <div role="presentation" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'> + <div role="presentation" class='dijitTabContent' data-dojo-attach-point='tabContent'> + <div role="presentation" data-dojo-attach-point='focusNode'> + <img src="${_blankGif}" alt="" class="dijitIcon dijitTabButtonIcon" data-dojo-attach-point='iconNode' /> + <span data-dojo-attach-point='containerNode' class='tabLabel'></span> + <span class="dijitInline dijitTabCloseButton dijitTabCloseIcon" data-dojo-attach-point='closeNode' + data-dojo-attach-event='onclick: onClickCloseButton' role="presentation"> + <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span + ></span> + </div> + </div> + </div> +</div> diff --git a/lib/dijit/layout/utils.js b/lib/dijit/layout/utils.js new file mode 100644 index 000000000..d0a4cd101 --- /dev/null +++ b/lib/dijit/layout/utils.js @@ -0,0 +1,2 @@ +//>>built +define("dijit/layout/utils",["dojo/_base/array","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/_base/lang",".."],function(_1,_2,_3,_4,_5,_6){var _7=_5.getObject("layout",true,_6);_7.marginBox2contentBox=function(_8,mb){var cs=_4.getComputedStyle(_8);var me=_3.getMarginExtents(_8,cs);var pb=_3.getPadBorderExtents(_8,cs);return {l:_4.toPixelValue(_8,cs.paddingLeft),t:_4.toPixelValue(_8,cs.paddingTop),w:mb.w-(me.w+pb.w),h:mb.h-(me.h+pb.h)};};function _9(_a){return _a.substring(0,1).toUpperCase()+_a.substring(1);};function _b(_c,_d){var _e=_c.resize?_c.resize(_d):_3.setMarginBox(_c.domNode,_d);if(_e){_5.mixin(_c,_e);}else{_5.mixin(_c,_3.getMarginBox(_c.domNode));_5.mixin(_c,_d);}};_7.layoutChildren=function(_f,dim,_10,_11,_12){dim=_5.mixin({},dim);_2.add(_f,"dijitLayoutContainer");_10=_1.filter(_10,function(_13){return _13.region!="center"&&_13.layoutAlign!="client";}).concat(_1.filter(_10,function(_14){return _14.region=="center"||_14.layoutAlign=="client";}));_1.forEach(_10,function(_15){var elm=_15.domNode,pos=(_15.region||_15.layoutAlign);if(!pos){throw new Error("No region setting for "+_15.id);}var _16=elm.style;_16.left=dim.l+"px";_16.top=dim.t+"px";_16.position="absolute";_2.add(elm,"dijitAlign"+_9(pos));var _17={};if(_11&&_11==_15.id){_17[_15.region=="top"||_15.region=="bottom"?"h":"w"]=_12;}if(pos=="top"||pos=="bottom"){_17.w=dim.w;_b(_15,_17);dim.h-=_15.h;if(pos=="top"){dim.t+=_15.h;}else{_16.top=dim.t+dim.h+"px";}}else{if(pos=="left"||pos=="right"){_17.h=dim.h;_b(_15,_17);dim.w-=_15.w;if(pos=="left"){dim.l+=_15.w;}else{_16.left=dim.l+dim.w+"px";}}else{if(pos=="client"||pos=="center"){_b(_15,dim);}}}});};return {marginBox2contentBox:_7.marginBox2contentBox,layoutChildren:_7.layoutChildren};});
\ No newline at end of file diff --git a/lib/dijit/layout/utils.js.uncompressed.js b/lib/dijit/layout/utils.js.uncompressed.js new file mode 100644 index 000000000..4c2845332 --- /dev/null +++ b/lib/dijit/layout/utils.js.uncompressed.js @@ -0,0 +1,141 @@ +define("dijit/layout/utils", [ + "dojo/_base/array", // array.filter array.forEach + "dojo/dom-class", // domClass.add domClass.remove + "dojo/dom-geometry", // domGeometry.marginBox + "dojo/dom-style", // domStyle.getComputedStyle + "dojo/_base/lang", // lang.mixin + ".." // for exporting symbols to dijit, remove in 2.0 +], function(array, domClass, domGeometry, domStyle, lang, dijit){ + + // module: + // dijit/layout/utils + // summary: + // marginBox2contentBox() and layoutChildren() + + var layout = lang.getObject("layout", true, dijit); + /*===== layout = dijit.layout =====*/ + + layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ + // summary: + // Given the margin-box size of a node, return its content box size. + // Functions like domGeometry.contentBox() but is more reliable since it doesn't have + // to wait for the browser to compute sizes. + var cs = domStyle.getComputedStyle(node); + var me = domGeometry.getMarginExtents(node, cs); + var pb = domGeometry.getPadBorderExtents(node, cs); + return { + l: domStyle.toPixelValue(node, cs.paddingLeft), + t: domStyle.toPixelValue(node, cs.paddingTop), + w: mb.w - (me.w + pb.w), + h: mb.h - (me.h + pb.h) + }; + }; + + function capitalize(word){ + return word.substring(0,1).toUpperCase() + word.substring(1); + } + + function size(widget, dim){ + // size the child + var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim); + + // record child's size + if(newSize){ + // if the child returned it's new size then use that + lang.mixin(widget, newSize); + }else{ + // otherwise, call getMarginBox(), but favor our own numbers when we have them. + // the browser lies sometimes + lang.mixin(widget, domGeometry.getMarginBox(widget.domNode)); + lang.mixin(widget, dim); + } + } + + layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children, + /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){ + // summary: + // Layout a bunch of child dom nodes within a parent dom node + // container: + // parent node + // dim: + // {l, t, w, h} object specifying dimensions of container into which to place children + // children: + // an array of Widgets or at least objects containing: + // * domNode: pointer to DOM node to position + // * region or layoutAlign: position to place DOM node + // * resize(): (optional) method to set size of node + // * id: (optional) Id of widgets, referenced from resize object, below. + // changedRegionId: + // If specified, the slider for the region with the specified id has been dragged, and thus + // the region's height or width should be adjusted according to changedRegionSize + // changedRegionSize: + // See changedRegionId. + + // copy dim because we are going to modify it + dim = lang.mixin({}, dim); + + domClass.add(container, "dijitLayoutContainer"); + + // Move "client" elements to the end of the array for layout. a11y dictates that the author + // needs to be able to put them in the document in tab-order, but this algorithm requires that + // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think. + children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; }) + .concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; })); + + // set positions/sizes + array.forEach(children, function(child){ + var elm = child.domNode, + pos = (child.region || child.layoutAlign); + if(!pos){ + throw new Error("No region setting for " + child.id) + } + + // set elem to upper left corner of unused space; may move it later + var elmStyle = elm.style; + elmStyle.left = dim.l+"px"; + elmStyle.top = dim.t+"px"; + elmStyle.position = "absolute"; + + domClass.add(elm, "dijitAlign" + capitalize(pos)); + + // Size adjustments to make to this child widget + var sizeSetting = {}; + + // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align + // panes and width adjustment for left/right align panes. + if(changedRegionId && changedRegionId == child.id){ + sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize; + } + + // set size && adjust record of remaining space. + // note that setting the width of a <div> may affect its height. + if(pos == "top" || pos == "bottom"){ + sizeSetting.w = dim.w; + size(child, sizeSetting); + dim.h -= child.h; + if(pos == "top"){ + dim.t += child.h; + }else{ + elmStyle.top = dim.t + dim.h + "px"; + } + }else if(pos == "left" || pos == "right"){ + sizeSetting.h = dim.h; + size(child, sizeSetting); + dim.w -= child.w; + if(pos == "left"){ + dim.l += child.w; + }else{ + elmStyle.left = dim.l + dim.w + "px"; + } + }else if(pos == "client" || pos == "center"){ + size(child, dim); + } + }); + }; + + + return { + marginBox2contentBox: layout.marginBox2contentBox, + layoutChildren: layout.layoutChildren + }; +}); |