284 lines
9.9 KiB
JavaScript
284 lines
9.9 KiB
JavaScript
|
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 lay out 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,
|
||
|
// like for example dijitTabContainer-content.
|
||
|
baseClass: "dijitLayoutContainer",
|
||
|
|
||
|
// isLayoutContainer: [private deprecated] Boolean
|
||
|
// TODO: this is unused, but maybe it *should* be used for a child to
|
||
|
// detect whether the parent is going to call resize() on it or not
|
||
|
// (see calls to getParent() and resize() in this file)
|
||
|
isLayoutContainer: true,
|
||
|
|
||
|
postCreate: function(){
|
||
|
dojo.addClass(this.domNode, "dijitContainer");
|
||
|
dojo.addClass(this.domNode, this.baseClass);
|
||
|
|
||
|
// TODO: this.inherited()
|
||
|
},
|
||
|
|
||
|
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; }
|
||
|
|
||
|
// TODO: seems like this code should be in _Container.startup().
|
||
|
// Then things that don't extend LayoutContainer (like GridContainer)
|
||
|
// would get the behavior for free.
|
||
|
dojo.forEach(this.getChildren(), function(child){ child.startup(); });
|
||
|
|
||
|
// If I am a top level widget
|
||
|
if(!this.getParent || !this.getParent()){
|
||
|
// 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 is width=height=100% (or something similar),
|
||
|
// then I need to watch when the window resizes, and size myself accordingly.
|
||
|
// (Passing in no arguments to resize means that it has to glean the size itself.)
|
||
|
// TODO: make one global listener to avoid getViewport() per widget.
|
||
|
this._viewport = dijit.getViewport();
|
||
|
this.connect(dojo.global, 'onresize', function(){
|
||
|
var newViewport = dijit.getViewport();
|
||
|
if(newViewport.w != this._viewport.w || newViewport.h != this._viewport.h){
|
||
|
this._viewport = newViewport;
|
||
|
this.resize();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.inherited(arguments);
|
||
|
},
|
||
|
|
||
|
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 it's 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 it's 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(/*Widget*/child){
|
||
|
// summary:
|
||
|
// Common setup for initial children and children which are added after startup
|
||
|
// tags:
|
||
|
// protected extension
|
||
|
|
||
|
dojo.addClass(child.domNode, this.baseClass+"-child");
|
||
|
if(child.baseClass){
|
||
|
dojo.addClass(child.domNode, this.baseClass+"-"+child.baseClass);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
|
||
|
// Overrides _Container.addChild() to call _setupChild()
|
||
|
this.inherited(arguments);
|
||
|
if(this._started){
|
||
|
this._setupChild(child);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
removeChild: function(/*Widget*/ child){
|
||
|
// Overrides _Container.removeChild() to remove class added by _setupChild()
|
||
|
dojo.removeClass(child.domNode, this.baseClass+"-child");
|
||
|
if(child.baseClass){
|
||
|
dojo.removeClass(child.domNode, this.baseClass+"-"+child.baseClass);
|
||
|
}
|
||
|
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
|
||
|
widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
|
||
|
|
||
|
// record child's size, 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, /*Object[]*/ children){
|
||
|
// 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 like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ]
|
||
|
|
||
|
// 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.
|
||
|
children = dojo.filter(children, function(item){ return item.layoutAlign != "client"; })
|
||
|
.concat(dojo.filter(children, function(item){ return item.layoutAlign == "client"; }));
|
||
|
|
||
|
// set positions/sizes
|
||
|
dojo.forEach(children, function(child){
|
||
|
var elm = child.domNode,
|
||
|
pos = 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.bottom = elmStyle.right = "auto";
|
||
|
|
||
|
dojo.addClass(elm, "dijitAlign" + capitalize(pos));
|
||
|
|
||
|
// set size && adjust record of remaining space.
|
||
|
// note that setting the width of a <div> may affect it's height.
|
||
|
if(pos == "top" || pos == "bottom"){
|
||
|
size(child, { w: dim.w });
|
||
|
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"){
|
||
|
size(child, { h: dim.h });
|
||
|
dim.w -= child.w;
|
||
|
if(pos == "left"){
|
||
|
dim.l += child.w;
|
||
|
}else{
|
||
|
elmStyle.left = dim.l + dim.w + "px";
|
||
|
}
|
||
|
}else if(pos == "client"){
|
||
|
size(child, dim);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
})();
|