]>
Commit | Line | Data |
---|---|---|
2f01fe57 | 1 | /* |
81bea17a | 2 | Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. |
2f01fe57 AD |
3 | Available via Academic Free License >= 2.1 OR the modified BSD license. |
4 | see: http://dojotoolkit.org/license for details | |
5 | */ | |
6 | ||
7 | ||
81bea17a AD |
8 | if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
9 | dojo._hasResource["dijit.layout._LayoutWidget"] = true; | |
2f01fe57 AD |
10 | dojo.provide("dijit.layout._LayoutWidget"); |
11 | dojo.require("dijit._Widget"); | |
12 | dojo.require("dijit._Container"); | |
13 | dojo.require("dijit._Contained"); | |
81bea17a AD |
14 | |
15 | ||
16 | dojo.declare("dijit.layout._LayoutWidget", | |
17 | [dijit._Widget, dijit._Container, dijit._Contained], | |
18 | { | |
19 | // summary: | |
20 | // Base class for a _Container widget which is responsible for laying out its children. | |
21 | // Widgets which mixin this code must define layout() to manage placement and sizing of the children. | |
22 | ||
23 | // baseClass: [protected extension] String | |
24 | // This class name is applied to the widget's domNode | |
25 | // and also may be used to generate names for sub nodes, | |
26 | // for example dijitTabContainer-content. | |
27 | baseClass: "dijitLayoutContainer", | |
28 | ||
29 | // isLayoutContainer: [protected] Boolean | |
30 | // Indicates that this widget is going to call resize() on its | |
31 | // children widgets, setting their size, when they become visible. | |
32 | isLayoutContainer: true, | |
33 | ||
34 | buildRendering: function(){ | |
35 | this.inherited(arguments); | |
36 | dojo.addClass(this.domNode, "dijitContainer"); | |
37 | }, | |
38 | ||
39 | startup: function(){ | |
40 | // summary: | |
41 | // Called after all the widgets have been instantiated and their | |
42 | // dom nodes have been inserted somewhere under dojo.doc.body. | |
43 | // | |
44 | // Widgets should override this method to do any initialization | |
45 | // dependent on other widgets existing, and then call | |
46 | // this superclass method to finish things off. | |
47 | // | |
48 | // startup() in subclasses shouldn't do anything | |
49 | // size related because the size of the widget hasn't been set yet. | |
50 | ||
51 | if(this._started){ return; } | |
52 | ||
53 | // Need to call inherited first - so that child widgets get started | |
54 | // up correctly | |
55 | this.inherited(arguments); | |
56 | ||
57 | // If I am a not being controlled by a parent layout widget... | |
58 | var parent = this.getParent && this.getParent() | |
59 | if(!(parent && parent.isLayoutContainer)){ | |
60 | // Do recursive sizing and layout of all my descendants | |
61 | // (passing in no argument to resize means that it has to glean the size itself) | |
62 | this.resize(); | |
63 | ||
64 | // Since my parent isn't a layout container, and my style *may be* width=height=100% | |
65 | // or something similar (either set directly or via a CSS class), | |
66 | // monitor when my size changes so that I can re-layout. | |
67 | // For browsers where I can't directly monitor when my size changes, | |
68 | // monitor when the viewport changes size, which *may* indicate a size change for me. | |
69 | this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){ | |
70 | // Using function(){} closure to ensure no arguments to resize. | |
71 | this.resize(); | |
72 | }); | |
73 | } | |
74 | }, | |
75 | ||
76 | resize: function(changeSize, resultSize){ | |
77 | // summary: | |
78 | // Call this to resize a widget, or after its size has changed. | |
79 | // description: | |
80 | // Change size mode: | |
81 | // When changeSize is specified, changes the marginBox of this widget | |
82 | // and forces it to relayout its contents accordingly. | |
83 | // changeSize may specify height, width, or both. | |
84 | // | |
85 | // If resultSize is specified it indicates the size the widget will | |
86 | // become after changeSize has been applied. | |
87 | // | |
88 | // Notification mode: | |
89 | // When changeSize is null, indicates that the caller has already changed | |
90 | // the size of the widget, or perhaps it changed because the browser | |
91 | // window was resized. Tells widget to relayout its contents accordingly. | |
92 | // | |
93 | // If resultSize is also specified it indicates the size the widget has | |
94 | // become. | |
95 | // | |
96 | // In either mode, this method also: | |
97 | // 1. Sets this._borderBox and this._contentBox to the new size of | |
98 | // the widget. Queries the current domNode size if necessary. | |
99 | // 2. Calls layout() to resize contents (and maybe adjust child widgets). | |
100 | // | |
101 | // changeSize: Object? | |
102 | // Sets the widget to this margin-box size and position. | |
103 | // May include any/all of the following properties: | |
104 | // | {w: int, h: int, l: int, t: int} | |
105 | // | |
106 | // resultSize: Object? | |
107 | // The margin-box size of this widget after applying changeSize (if | |
108 | // changeSize is specified). If caller knows this size and | |
109 | // passes it in, we don't need to query the browser to get the size. | |
110 | // | {w: int, h: int} | |
111 | ||
112 | var node = this.domNode; | |
113 | ||
114 | // set margin box size, unless it wasn't specified, in which case use current size | |
115 | if(changeSize){ | |
116 | dojo.marginBox(node, changeSize); | |
117 | ||
118 | // set offset of the node | |
119 | if(changeSize.t){ node.style.top = changeSize.t + "px"; } | |
120 | if(changeSize.l){ node.style.left = changeSize.l + "px"; } | |
121 | } | |
122 | ||
123 | // If either height or width wasn't specified by the user, then query node for it. | |
124 | // But note that setting the margin box and then immediately querying dimensions may return | |
125 | // inaccurate results, so try not to depend on it. | |
126 | var mb = resultSize || {}; | |
127 | dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize | |
128 | if( !("h" in mb) || !("w" in mb) ){ | |
129 | mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values | |
130 | } | |
131 | ||
132 | // Compute and save the size of my border box and content box | |
133 | // (w/out calling dojo.contentBox() since that may fail if size was recently set) | |
134 | var cs = dojo.getComputedStyle(node); | |
135 | var me = dojo._getMarginExtents(node, cs); | |
136 | var be = dojo._getBorderExtents(node, cs); | |
137 | var bb = (this._borderBox = { | |
138 | w: mb.w - (me.w + be.w), | |
139 | h: mb.h - (me.h + be.h) | |
140 | }); | |
141 | var pe = dojo._getPadExtents(node, cs); | |
142 | this._contentBox = { | |
143 | l: dojo._toPixelValue(node, cs.paddingLeft), | |
144 | t: dojo._toPixelValue(node, cs.paddingTop), | |
145 | w: bb.w - pe.w, | |
146 | h: bb.h - pe.h | |
147 | }; | |
148 | ||
149 | // Callback for widget to adjust size of its children | |
150 | this.layout(); | |
151 | }, | |
152 | ||
153 | layout: function(){ | |
154 | // summary: | |
155 | // Widgets override this method to size and position their contents/children. | |
156 | // When this is called this._contentBox is guaranteed to be set (see resize()). | |
157 | // | |
158 | // This is called after startup(), and also when the widget's size has been | |
159 | // changed. | |
160 | // tags: | |
161 | // protected extension | |
162 | }, | |
163 | ||
164 | _setupChild: function(/*dijit._Widget*/child){ | |
165 | // summary: | |
166 | // Common setup for initial children and children which are added after startup | |
167 | // tags: | |
168 | // protected extension | |
169 | ||
170 | var cls = this.baseClass + "-child " | |
171 | + (child.baseClass ? this.baseClass + "-" + child.baseClass : ""); | |
172 | dojo.addClass(child.domNode, cls); | |
173 | }, | |
174 | ||
175 | addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ | |
176 | // Overrides _Container.addChild() to call _setupChild() | |
177 | this.inherited(arguments); | |
178 | if(this._started){ | |
179 | this._setupChild(child); | |
180 | } | |
181 | }, | |
182 | ||
183 | removeChild: function(/*dijit._Widget*/ child){ | |
184 | // Overrides _Container.removeChild() to remove class added by _setupChild() | |
185 | var cls = this.baseClass + "-child" | |
186 | + (child.baseClass ? | |
187 | " " + this.baseClass + "-" + child.baseClass : ""); | |
188 | dojo.removeClass(child.domNode, cls); | |
189 | ||
190 | this.inherited(arguments); | |
191 | } | |
192 | } | |
193 | ); | |
194 | ||
195 | dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ | |
196 | // summary: | |
197 | // Given the margin-box size of a node, return its content box size. | |
198 | // Functions like dojo.contentBox() but is more reliable since it doesn't have | |
199 | // to wait for the browser to compute sizes. | |
200 | var cs = dojo.getComputedStyle(node); | |
201 | var me = dojo._getMarginExtents(node, cs); | |
202 | var pb = dojo._getPadBorderExtents(node, cs); | |
203 | return { | |
204 | l: dojo._toPixelValue(node, cs.paddingLeft), | |
205 | t: dojo._toPixelValue(node, cs.paddingTop), | |
206 | w: mb.w - (me.w + pb.w), | |
207 | h: mb.h - (me.h + pb.h) | |
208 | }; | |
2f01fe57 | 209 | }; |
81bea17a | 210 | |
2f01fe57 | 211 | (function(){ |
81bea17a AD |
212 | var capitalize = function(word){ |
213 | return word.substring(0,1).toUpperCase() + word.substring(1); | |
214 | }; | |
215 | ||
216 | var size = function(widget, dim){ | |
217 | // size the child | |
218 | var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim); | |
219 | ||
220 | // record child's size | |
221 | if(newSize){ | |
222 | // if the child returned it's new size then use that | |
223 | dojo.mixin(widget, newSize); | |
224 | }else{ | |
225 | // otherwise, call marginBox(), but favor our own numbers when we have them. | |
226 | // the browser lies sometimes | |
227 | dojo.mixin(widget, dojo.marginBox(widget.domNode)); | |
228 | dojo.mixin(widget, dim); | |
229 | } | |
230 | }; | |
231 | ||
232 | dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children, | |
233 | /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){ | |
234 | // summary | |
235 | // Layout a bunch of child dom nodes within a parent dom node | |
236 | // container: | |
237 | // parent node | |
238 | // dim: | |
239 | // {l, t, w, h} object specifying dimensions of container into which to place children | |
240 | // children: | |
241 | // an array of Widgets or at least objects containing: | |
242 | // * domNode: pointer to DOM node to position | |
243 | // * region or layoutAlign: position to place DOM node | |
244 | // * resize(): (optional) method to set size of node | |
245 | // * id: (optional) Id of widgets, referenced from resize object, below. | |
246 | // changedRegionId: | |
247 | // If specified, the slider for the region with the specified id has been dragged, and thus | |
248 | // the region's height or width should be adjusted according to changedRegionSize | |
249 | // changedRegionSize: | |
250 | // See changedRegionId. | |
251 | ||
252 | // copy dim because we are going to modify it | |
253 | dim = dojo.mixin({}, dim); | |
254 | ||
255 | dojo.addClass(container, "dijitLayoutContainer"); | |
256 | ||
257 | // Move "client" elements to the end of the array for layout. a11y dictates that the author | |
258 | // needs to be able to put them in the document in tab-order, but this algorithm requires that | |
259 | // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think. | |
260 | children = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; }) | |
261 | .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; })); | |
262 | ||
263 | // set positions/sizes | |
264 | dojo.forEach(children, function(child){ | |
265 | var elm = child.domNode, | |
266 | pos = (child.region || child.layoutAlign); | |
267 | ||
268 | // set elem to upper left corner of unused space; may move it later | |
269 | var elmStyle = elm.style; | |
270 | elmStyle.left = dim.l+"px"; | |
271 | elmStyle.top = dim.t+"px"; | |
272 | elmStyle.position = "absolute"; | |
273 | ||
274 | dojo.addClass(elm, "dijitAlign" + capitalize(pos)); | |
275 | ||
276 | // Size adjustments to make to this child widget | |
277 | var sizeSetting = {}; | |
278 | ||
279 | // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align | |
280 | // panes and width adjustment for left/right align panes. | |
281 | if(changedRegionId && changedRegionId == child.id){ | |
282 | sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize; | |
283 | } | |
284 | ||
285 | // set size && adjust record of remaining space. | |
286 | // note that setting the width of a <div> may affect its height. | |
287 | if(pos == "top" || pos == "bottom"){ | |
288 | sizeSetting.w = dim.w; | |
289 | size(child, sizeSetting); | |
290 | dim.h -= child.h; | |
291 | if(pos == "top"){ | |
292 | dim.t += child.h; | |
293 | }else{ | |
294 | elmStyle.top = dim.t + dim.h + "px"; | |
295 | } | |
296 | }else if(pos == "left" || pos == "right"){ | |
297 | sizeSetting.h = dim.h; | |
298 | size(child, sizeSetting); | |
299 | dim.w -= child.w; | |
300 | if(pos == "left"){ | |
301 | dim.l += child.w; | |
302 | }else{ | |
303 | elmStyle.left = dim.l + dim.w + "px"; | |
304 | } | |
305 | }else if(pos == "client" || pos == "center"){ | |
306 | size(child, dim); | |
307 | } | |
308 | }); | |
309 | }; | |
310 | ||
2f01fe57 | 311 | })(); |
81bea17a | 312 | |
2f01fe57 | 313 | } |