diff options
Diffstat (limited to 'lib/dijit/layout/_LayoutWidget.js')
-rw-r--r-- | lib/dijit/layout/_LayoutWidget.js | 411 |
1 files changed, 299 insertions, 112 deletions
diff --git a/lib/dijit/layout/_LayoutWidget.js b/lib/dijit/layout/_LayoutWidget.js index ec7be37b7..cbbf71009 100644 --- a/lib/dijit/layout/_LayoutWidget.js +++ b/lib/dijit/layout/_LayoutWidget.js @@ -1,126 +1,313 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + 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"]){ -dojo._hasResource["dijit.layout._LayoutWidget"]=true; +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],{baseClass:"dijitLayoutContainer",isLayoutContainer:true,postCreate:function(){ -dojo.addClass(this.domNode,"dijitContainer"); -this.inherited(arguments); -},startup:function(){ -if(this._started){ -return; -} -this.inherited(arguments); -var _1=this.getParent&&this.getParent(); -if(!(_1&&_1.isLayoutContainer)){ -this.resize(); -this.connect(dojo.isIE?this.domNode:dojo.global,"onresize",function(){ -this.resize(); -}); -} -},resize:function(_2,_3){ -var _4=this.domNode; -if(_2){ -dojo.marginBox(_4,_2); -if(_2.t){ -_4.style.top=_2.t+"px"; -} -if(_2.l){ -_4.style.left=_2.l+"px"; -} -} -var mb=_3||{}; -dojo.mixin(mb,_2||{}); -if(!("h" in mb)||!("w" in mb)){ -mb=dojo.mixin(dojo.marginBox(_4),mb); -} -var cs=dojo.getComputedStyle(_4); -var me=dojo._getMarginExtents(_4,cs); -var be=dojo._getBorderExtents(_4,cs); -var bb=(this._borderBox={w:mb.w-(me.w+be.w),h:mb.h-(me.h+be.h)}); -var pe=dojo._getPadExtents(_4,cs); -this._contentBox={l:dojo._toPixelValue(_4,cs.paddingLeft),t:dojo._toPixelValue(_4,cs.paddingTop),w:bb.w-pe.w,h:bb.h-pe.h}; -this.layout(); -},layout:function(){ -},_setupChild:function(_5){ -dojo.addClass(_5.domNode,this.baseClass+"-child"); -if(_5.baseClass){ -dojo.addClass(_5.domNode,this.baseClass+"-"+_5.baseClass); -} -},addChild:function(_6,_7){ -this.inherited(arguments); -if(this._started){ -this._setupChild(_6); -} -},removeChild:function(_8){ -dojo.removeClass(_8.domNode,this.baseClass+"-child"); -if(_8.baseClass){ -dojo.removeClass(_8.domNode,this.baseClass+"-"+_8.baseClass); -} -this.inherited(arguments); -}}); -dijit.layout.marginBox2contentBox=function(_9,mb){ -var cs=dojo.getComputedStyle(_9); -var me=dojo._getMarginExtents(_9,cs); -var pb=dojo._getPadBorderExtents(_9,cs); -return {l:dojo._toPixelValue(_9,cs.paddingLeft),t:dojo._toPixelValue(_9,cs.paddingTop),w:mb.w-(me.w+pb.w),h:mb.h-(me.h+pb.h)}; + + +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 _a=function(_b){ -return _b.substring(0,1).toUpperCase()+_b.substring(1); -}; -var _c=function(_d,_e){ -_d.resize?_d.resize(_e):dojo.marginBox(_d.domNode,_e); -dojo.mixin(_d,dojo.marginBox(_d.domNode)); -dojo.mixin(_d,_e); -}; -dijit.layout.layoutChildren=function(_f,dim,_10){ -dim=dojo.mixin({},dim); -dojo.addClass(_f,"dijitLayoutContainer"); -_10=dojo.filter(_10,function(_11){ -return _11.layoutAlign!="client"; -}).concat(dojo.filter(_10,function(_12){ -return _12.layoutAlign=="client"; -})); -dojo.forEach(_10,function(_13){ -var elm=_13.domNode,pos=_13.layoutAlign; -var _14=elm.style; -_14.left=dim.l+"px"; -_14.top=dim.t+"px"; -_14.bottom=_14.right="auto"; -dojo.addClass(elm,"dijitAlign"+_a(pos)); -if(pos=="top"||pos=="bottom"){ -_c(_13,{w:dim.w}); -dim.h-=_13.h; -if(pos=="top"){ -dim.t+=_13.h; -}else{ -_14.top=dim.t+dim.h+"px"; -} -}else{ -if(pos=="left"||pos=="right"){ -_c(_13,{h:dim.h}); -dim.w-=_13.w; -if(pos=="left"){ -dim.l+=_13.w; -}else{ -_14.left=dim.l+dim.w+"px"; -} -}else{ -if(pos=="client"){ -_c(_13,dim); -} -} -} -}); -}; + 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); + } + }); + }; + })(); + } |