]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/layout/BorderContainer.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / layout / BorderContainer.js
1 if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit.layout.BorderContainer"] = true;
3 dojo.provide("dijit.layout.BorderContainer");
4
5 dojo.require("dijit.layout._LayoutWidget");
6 dojo.require("dojo.cookie");
7
8 dojo.declare(
9         "dijit.layout.BorderContainer",
10 //      [dijit._Widget, dijit._Container, dijit._Contained],
11         dijit.layout._LayoutWidget,
12 {
13         // summary:
14         //      Provides layout in 5 regions, a center and borders along its 4 sides.
15         //
16         // description:
17         //      A BorderContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
18         //      that contains a child widget marked region="center" and optionally children widgets marked
19         //      region equal to "top", "bottom", "leading", "trailing", "left" or "right".
20         //      Children along the edges will be laid out according to width or height dimensions.  The remaining
21         //      space is designated for the center region.
22         //      The outer size must be specified on the BorderContainer node.  Width must be specified for the sides
23         //  and height for the top and bottom, respectively.  No dimensions should be specified on the center;
24         //      it will fill the remaining space.  Regions named "leading" and "trailing" may be used just like
25         //      "left" and "right" except that they will be reversed in right-to-left environments.
26         //  Optional splitters may be specified on the edge widgets only to make them resizable by the user.
27         //
28         // example:
29         // |    <style>
30         // |            html, body { height: 100%; width: 100%; }
31         // |    </style>
32         // |    <div dojoType="BorderContainer" design="sidebar" style="width: 100%; height: 100%">
33         // |            <div dojoType="ContentPane" region="top">header text</div>
34         // |            <div dojoType="ContentPane" region="right" style="width: 200px;">table of contents</div>
35         // |            <div dojoType="ContentPane" region="center">client area</div>
36         // |    </div>
37         //
38         // design: String
39         //  choose which design is used for the layout: "headline" (default) where the top and bottom extend
40         //  the full width of the container, or "sidebar" where the left and right sides extend from top to bottom.
41         design: "headline",
42
43         // liveSplitters: Boolean
44         //  specifies whether splitters resize as you drag (true) or only upon mouseup (false)
45         liveSplitters: true,
46
47         // persist: Boolean
48         //              Save splitter positions in a cookie.
49         persist: false, // Boolean
50
51         // _splitterClass: String
52         //              Optional hook to override the default Splitter widget used by BorderContainer
53         _splitterClass: "dijit.layout._Splitter",
54
55         postCreate: function(){
56                 this.inherited(arguments);
57
58                 this._splitters = {};
59                 this._splitterThickness = {};
60                 dojo.addClass(this.domNode, "dijitBorderContainer");
61         },
62
63         startup: function(){
64                 if(this._started){ return; }
65                 dojo.forEach(this.getChildren(), this._setupChild, this);
66                 this.inherited(arguments);
67         },
68
69         _setupChild: function(/*Widget*/child){
70                 var region = child.region;
71                 if(region){
72 //                      dojo.addClass(child.domNode, "dijitBorderContainerPane");
73                         child.domNode.style.position = "absolute"; // bill says not to set this in CSS, since we can't keep others
74                                 // from destroying the class list
75
76                         var ltr = this.isLeftToRight();
77                         if(region == "leading"){ region = ltr ? "left" : "right"; }
78                         if(region == "trailing"){ region = ltr ? "right" : "left"; }
79
80                         this["_"+region] = child.domNode;
81                         this["_"+region+"Widget"] = child;
82
83                         if(child.splitter){
84                                 var _Splitter = dojo.getObject(this._splitterClass);
85                                 var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'};
86                                 var oppNodeList = dojo.query('[region=' + flip[child.region] + ']', this.domNode);
87                                 var splitter = new _Splitter({ container: this, child: child, region: region,
88                                         oppNode: oppNodeList[0], live: this.liveSplitters });
89                                 this._splitters[region] = splitter.domNode;
90                                 dojo.place(splitter.domNode, child.domNode, "after");
91                                 this._computeSplitterThickness(region);
92                         }
93                         child.region = region;
94                 }
95         },
96
97         _computeSplitterThickness: function(region){
98                 var re = new RegExp("top|bottom");
99                 this._splitterThickness[region] =
100                         dojo.marginBox(this._splitters[region])[(re.test(region) ? 'h' : 'w')];
101         },
102
103         layout: function(){
104                 this._layoutChildren();
105         },
106
107         addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
108                 this.inherited(arguments);
109                 this._setupChild(child);
110                 if(this._started){
111                         this._layoutChildren(); //OPT
112                 }
113         },
114
115         removeChild: function(/*Widget*/ child){
116                 var region = child.region;
117                 var splitter = this._splitters[region];
118                 if(splitter){
119                         dijit.byNode(splitter).destroy();
120                         delete this._splitters[region];
121                         delete this._splitterThickness[region];
122                 }
123                 this.inherited(arguments);
124                 delete this["_"+region];
125                 delete this["_" +region+"Widget"];
126                 if(this._started){
127                         this._layoutChildren(child.region);
128                 }
129         },
130
131         _layoutChildren: function(/*String?*/changedRegion){
132                 var sidebarLayout = (this.design == "sidebar");
133                 var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0;
134                 var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {},
135                         centerStyle = (this._center && this._center.style) || {};
136
137                 var changedSide = /left|right/.test(changedRegion);
138
139                 var layoutSides = !changedRegion || (!changedSide && !sidebarLayout);
140                 var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout);
141                 if(this._top){
142                         topStyle = layoutTopBottom && this._top.style;
143                         topHeight = dojo.marginBox(this._top).h;
144                 }
145                 if(this._left){
146                         leftStyle = layoutSides && this._left.style;
147                         leftWidth = dojo.marginBox(this._left).w;
148                 }
149                 if(this._right){
150                         rightStyle = layoutSides && this._right.style;
151                         rightWidth = dojo.marginBox(this._right).w;
152                 }
153                 if(this._bottom){
154                         bottomStyle = layoutTopBottom && this._bottom.style;
155                         bottomHeight = dojo.marginBox(this._bottom).h;
156                 }
157
158                 var splitters = this._splitters;
159                 var topSplitter = splitters.top;
160                 var bottomSplitter = splitters.bottom;
161                 var leftSplitter = splitters.left;
162                 var rightSplitter = splitters.right;
163                 var splitterThickness = this._splitterThickness;
164                 var topSplitterThickness = splitterThickness.top || 0;
165                 var leftSplitterThickness = splitterThickness.left || 0;
166                 var rightSplitterThickness = splitterThickness.right || 0;
167                 var bottomSplitterThickness = splitterThickness.bottom || 0;
168
169                 // Check for race condition where CSS hasn't finished loading, so
170                 // the splitter width == the viewport width (#5824)
171                 if(leftSplitterThickness > 50 || rightSplitterThickness > 50){
172                         setTimeout(dojo.hitch(this, function(){
173                                 for(var region in this._splitters){
174                                         this._computeSplitterThickness(region);
175                                 }
176                                 this._layoutChildren();
177                         }), 50);
178                         return false;
179                 }
180
181                 var splitterBounds = {
182                         left: (sidebarLayout ? leftWidth + leftSplitterThickness: "0") + "px",
183                         right: (sidebarLayout ? rightWidth + rightSplitterThickness: "0") + "px"
184                 };
185
186                 if(topSplitter){
187                         dojo.mixin(topSplitter.style, splitterBounds);
188                         topSplitter.style.top = topHeight + "px";
189                 }
190
191                 if(bottomSplitter){
192                         dojo.mixin(bottomSplitter.style, splitterBounds);
193                         bottomSplitter.style.bottom = bottomHeight + "px";
194                 }
195
196                 splitterBounds = {
197                         top: (sidebarLayout ? "0" : topHeight + topSplitterThickness) + "px",
198                         bottom: (sidebarLayout ? "0" : bottomHeight + bottomSplitterThickness) + "px"
199                 };
200
201                 if(leftSplitter){
202                         dojo.mixin(leftSplitter.style, splitterBounds);
203                         leftSplitter.style.left = leftWidth + "px";
204                 }
205
206                 if(rightSplitter){
207                         dojo.mixin(rightSplitter.style, splitterBounds);
208                         rightSplitter.style.right = rightWidth + "px";
209                 }
210
211                 dojo.mixin(centerStyle, {
212                         top: topHeight + topSplitterThickness + "px",
213                         left: leftWidth + leftSplitterThickness + "px",
214                         right:  rightWidth + rightSplitterThickness + "px",
215                         bottom: bottomHeight + bottomSplitterThickness + "px"
216                 });
217
218                 var bounds = {
219                         top: sidebarLayout ? "0" : centerStyle.top,
220                         bottom: sidebarLayout ? "0" : centerStyle.bottom
221                 };
222                 dojo.mixin(leftStyle, bounds);
223                 dojo.mixin(rightStyle, bounds);
224                 leftStyle.left = rightStyle.right = topStyle.top = bottomStyle.bottom = "0";
225                 if(sidebarLayout){
226                         topStyle.left = bottomStyle.left = leftWidth + (this.isLeftToRight() ? leftSplitterThickness : 0) + "px";
227                         topStyle.right = bottomStyle.right = rightWidth + (this.isLeftToRight() ? 0 : rightSplitterThickness) + "px";
228                 }else{
229                         topStyle.left = topStyle.right = bottomStyle.left = bottomStyle.right = "0";
230                 }
231
232                 // Nodes in IE respond to t/l/b/r, and TEXTAREA doesn't respond in any browser
233                 var janky = dojo.isIE || dojo.some(this.getChildren(), function(child){
234                         return child.domNode.tagName == "TEXTAREA";
235                 });
236                 if(janky){
237                         // Set the size of the children the old fashioned way, by calling
238                         // childNode.resize({h: int, w: int}) for each child node)
239
240                         var borderBox = function(n, b){
241                                 n=dojo.byId(n);
242                                 var s = dojo.getComputedStyle(n);
243                                 if(!b){ return dojo._getBorderBox(n, s); }
244                                 var me = dojo._getMarginExtents(n, s);
245                                 dojo._setMarginBox(n, b.l, b.t, b.w + me.w, b.h + me.h, s);
246                                 return null;
247                         };
248
249                         var resizeWidget = function(widget, dim){
250                                 if(widget){
251                                         widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
252                                 }
253                         };
254
255                         // TODO: use dim passed in to resize() (see _LayoutWidget.js resize())
256                         // Then can make borderBox setBorderBox(), since no longer need to ever get the borderBox() size
257                         var thisBorderBox = borderBox(this.domNode);
258
259                         var containerHeight = thisBorderBox.h;
260                         var middleHeight = containerHeight;
261                         if(this._top){ middleHeight -= topHeight; }
262                         if(this._bottom){ middleHeight -= bottomHeight; }
263                         if(topSplitter){ middleHeight -= topSplitterThickness; }
264                         if(bottomSplitter){ middleHeight -= bottomSplitterThickness; }
265                         var centerDim = { h: middleHeight };
266
267                         var sidebarHeight = sidebarLayout ? containerHeight : middleHeight;
268                         if(leftSplitter){ leftSplitter.style.height = sidebarHeight; }
269                         if(rightSplitter){ rightSplitter.style.height = sidebarHeight; }
270                         resizeWidget(this._leftWidget, {h: sidebarHeight});
271                         resizeWidget(this._rightWidget, {h: sidebarHeight});
272
273                         var containerWidth = thisBorderBox.w;
274                         var middleWidth = containerWidth;
275                         if(this._left){ middleWidth -= leftWidth; }
276                         if(this._right){ middleWidth -= rightWidth; }
277                         if(leftSplitter){ middleWidth -= leftSplitterThickness; }
278                         if(rightSplitter){ middleWidth -= rightSplitterThickness; }
279                         centerDim.w = middleWidth;
280
281                         var sidebarWidth = sidebarLayout ? middleWidth : containerWidth;
282                         if(topSplitter){ topSplitter.style.width = sidebarWidth; }
283                         if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; }
284                         resizeWidget(this._topWidget, {w: sidebarWidth});
285                         resizeWidget(this._bottomWidget, {w: sidebarWidth});
286
287                         resizeWidget(this._centerWidget, centerDim);
288                 }else{
289
290                         // We've already sized the children by setting style.top/bottom/left/right...
291                         // Now just need to call resize() on those children so they can re-layout themselves
292
293                         // TODO: calling child.resize() without an argument is bad, because it forces
294                         // the child to query it's own size (even though this function already knows
295                         // the size), plus which querying the size of a node right after setting it
296                         // is known to cause problems (incorrect answer or an exception).
297                         // This is a setback from older layout widgets, which
298                         // don't do that.  See #3399, #2678, #3624 and #2955, #1988
299
300                         var resizeList = {};
301                         if(changedRegion){
302                                 resizeList[changedRegion] = resizeList.center = true;
303                                 if(/top|bottom/.test(changedRegion) && this.design != "sidebar"){
304                                         resizeList.left = resizeList.right = true;
305                                 }else if(/left|right/.test(changedRegion) && this.design == "sidebar"){
306                                         resizeList.top = resizeList.bottom = true;
307                                 }
308                         }
309
310                         dojo.forEach(this.getChildren(), function(child){
311                                 if(child.resize && (!changedRegion || child.region in resizeList)){
312         //                              console.log(this.id, ": resizing child id=" + child.id + " (region=" + child.region + "), style before resize is " +
313         //                                                                       "{ t: " + child.domNode.style.top +
314         //                                                                      ", b: " + child.domNode.style.bottom +
315         //                                                                      ", l: " + child.domNode.style.left +
316         //                                                                       ", r: " + child.domNode.style.right +
317         //                                                                       ", w: " + child.domNode.style.width +
318         //                                                                       ", h: " + child.domNode.style.height +
319         //                                                                      "}"
320         //                                              );
321                                         child.resize();
322         //                              console.log(this.id, ": after resize of child id=" + child.id + " (region=" + child.region + ") " +
323         //                                                                       "{ t: " + child.domNode.style.top +
324         //                                                                      ", b: " + child.domNode.style.bottom +
325         //                                                                      ", l: " + child.domNode.style.left +
326         //                                                                       ", r: " + child.domNode.style.right +
327         //                                                                       ", w: " + child.domNode.style.width +
328         //                                                                       ", h: " + child.domNode.style.height +
329         //                                                                      "}"
330         //                                              );
331                                 }
332                         }, this);
333                 }
334         }
335 });
336
337 // This argument can be specified for the children of a BorderContainer.
338 // Since any widget can be specified as a LayoutContainer child, mix it
339 // into the base widget class.  (This is a hack, but it's effective.)
340 dojo.extend(dijit._Widget, {
341         // region: String
342         //              "top", "bottom", "leading", "trailing", "left", "right", "center".
343         //              See the BorderContainer description for details on this parameter.
344         region: '',
345
346         // splitter: Boolean
347         splitter: false,
348
349         // minSize: Number
350         minSize: 0,
351
352         // maxSize: Number
353         maxSize: Infinity
354 });
355
356 dojo.require("dijit._Templated");
357
358 dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
359 {
360 /*=====
361         container: null,
362         child: null,
363         region: null,
364 =====*/
365
366         // live: Boolean
367         //              If true, the child's size changes and the child widget is redrawn as you drag the splitter;
368         //              otherwise, the size doesn't change until you drop the splitter (by mouse-up)
369         live: true,
370
371         // summary: A draggable spacer between two items in a BorderContainer
372         templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag" tabIndex="0" waiRole="separator"><div class="dijitSplitterThumb"></div></div>',
373
374         postCreate: function(){
375                 this.inherited(arguments);
376                 this.horizontal = /top|bottom/.test(this.region);
377                 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
378 //              dojo.addClass(this.child.domNode, "dijitSplitterPane");
379 //              dojo.setSelectable(this.domNode, false); //TODO is this necessary?
380
381                 this._factor = /top|left/.test(this.region) ? 1 : -1;
382                 this._minSize = this.child.minSize;
383
384                 this._computeMaxSize();
385                 //TODO: might be more accurate to recompute constraints on resize?
386                 this.connect(this.container, "layout", dojo.hitch(this, this._computeMaxSize));
387
388                 this._cookieName = this.container.id + "_" + this.region;
389                 if(this.container.persist){
390                         // restore old size
391                         var persistSize = dojo.cookie(this._cookieName);
392                         if(persistSize){
393                                 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
394                         }
395                 }
396         },
397
398         _computeMaxSize: function(){
399                 var dim = this.horizontal ? 'h' : 'w';
400                 var available = dojo.contentBox(this.container.domNode)[dim] - (this.oppNode ? dojo.marginBox(this.oppNode)[dim] : 0);
401                 this._maxSize = Math.min(this.child.maxSize, available);
402         },
403
404         _startDrag: function(e){
405                 if(!this.cover){
406                         this.cover = dojo.doc.createElement('div');
407                         dojo.addClass(this.cover, "dijitSplitterCover");
408                         dojo.place(this.cover, this.child.domNode, "after");
409                 }else{
410                         this.cover.style.zIndex = 1;
411                 }
412
413                 // Safeguard in case the stop event was missed.  Shouldn't be necessary if we always get the mouse up. 
414                 if(this.fake){ dojo._destroyElement(this.fake); }
415                 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
416                         // create fake splitter to display at old position while we drag
417                         (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
418                         dojo.addClass(this.domNode, "dijitSplitterShadow");
419                         dojo.place(this.fake, this.domNode, "after");
420                 }
421                 dojo.addClass(this.domNode, "dijitSplitterActive");
422
423                 //Performance: load data info local vars for onmousevent function closure
424                 var factor = this._factor,
425                         max = this._maxSize,
426                         min = this._minSize || 10;
427                 var axis = this.horizontal ? "pageY" : "pageX";
428                 var pageStart = e[axis];
429                 var splitterStyle = this.domNode.style;
430                 var dim = this.horizontal ? 'h' : 'w';
431                 var childStart = dojo.marginBox(this.child.domNode)[dim];
432                 var splitterStart = parseInt(this.domNode.style[this.region]);
433                 var resize = this._resize;
434                 var region = this.region;
435                 var mb = {};
436                 var childNode = this.child.domNode;
437                 var layoutFunc = dojo.hitch(this.container, this.container._layoutChildren);
438
439                 var de = dojo.doc.body;
440                 this._handlers = (this._handlers || []).concat([
441                         dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
442                                 var delta = e[axis] - pageStart,
443                                         childSize = factor * delta + childStart,
444                                         boundChildSize = Math.max(Math.min(childSize, max), min);
445
446                                 if(resize || forceResize){
447                                         mb[dim] = boundChildSize;
448                                         // TODO: inefficient; we set the marginBox here and then immediately layoutFunc() needs to query it
449                                         dojo.marginBox(childNode, mb);
450                                         layoutFunc(region);
451                                 }
452                                 splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px";
453                         }),
454                         dojo.connect(de, "onmouseup", this, "_stopDrag")
455                 ]);
456                 dojo.stopEvent(e);
457         },
458
459         _stopDrag: function(e){
460                 try{
461                         if(this.cover){ this.cover.style.zIndex = -1; }
462                         if(this.fake){ dojo._destroyElement(this.fake); }
463                         dojo.removeClass(this.domNode, "dijitSplitterActive");
464                         dojo.removeClass(this.domNode, "dijitSplitterShadow");
465                         this._drag(e); //TODO: redundant with onmousemove?
466                         this._drag(e, true);
467                 }finally{
468                         this._cleanupHandlers();
469                         delete this._drag;
470                 }
471
472                 if(this.container.persist){
473                         dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"]);
474                 }
475         },
476
477         _cleanupHandlers: function(){
478                 dojo.forEach(this._handlers, dojo.disconnect);
479                 delete this._handlers;
480         },
481
482         _onKeyPress: function(/*Event*/ e){
483                 // should we apply typematic to this?
484                 this._resize = true;
485                 var horizontal = this.horizontal;
486                 var tick = 1;
487                 var dk = dojo.keys;
488                 switch(e.keyCode){
489                         case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
490                                 tick *= -1;
491                                 break;
492                         case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
493                                 break;
494                         default:
495 //                              this.inherited(arguments);
496                                 return;
497                 }
498                 var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
499                 var mb = {};
500                 mb[ this.horizontal ? "h" : "w"] = Math.max(Math.min(childSize, this._maxSize), this._minSize);
501                 dojo.marginBox(this.child.domNode, mb);
502                 this.container._layoutChildren(this.region);
503                 dojo.stopEvent(e);
504         },
505
506         destroy: function(){
507                 this._cleanupHandlers();
508                 delete this.child;
509                 delete this.container;
510                 delete this.fake;
511                 this.inherited(arguments);
512         }
513 });
514
515 }