]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/layout/_LayoutWidget.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / layout / _LayoutWidget.js
1 /*
2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5 */
6
7
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;
10 dojo.provide("dijit.layout._LayoutWidget");
11 dojo.require("dijit._Widget");
12 dojo.require("dijit._Container");
13 dojo.require("dijit._Contained");
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 };
209 };
210
211 (function(){
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
311 })();
312
313 }