1 if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit.layout.SplitContainer"] = true;
3 dojo.provide("dijit.layout.SplitContainer");
6 // FIXME: make it prettier
7 // FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
10 dojo.require("dojo.cookie");
11 dojo.require("dijit.layout._LayoutWidget");
13 dojo.declare("dijit.layout.SplitContainer",
14 dijit.layout._LayoutWidget,
17 // A Container widget with sizing handles in-between each child
19 // Contains multiple children widgets, all of which are displayed side by side
20 // (either horizontally or vertically); there's a bar between each of the children,
21 // and you can adjust the relative size of each child by dragging the bars.
23 // You must specify a size (width and height) for the SplitContainer.
25 constructor: function(){
26 dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
29 // activeSizing: Boolean
30 // If true, the children's size changes as you drag the bar;
31 // otherwise, the sizes don't change until you drop the bar (by mouse-up)
34 // sizerWidth: Integer
35 // Size in pixels of the bar between each child
36 sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
38 // orientation: String
39 // either 'horizontal' or vertical; indicates whether the children are
40 // arranged side-by-side or up/down.
41 orientation: 'horizontal',
44 // Save splitter positions in a cookie
47 postMixInProperties: function(){
48 this.inherited("postMixInProperties",arguments);
49 this.isHorizontal = (this.orientation == 'horizontal');
52 postCreate: function(){
53 this.inherited("postCreate",arguments);
55 dojo.addClass(this.domNode, "dijitSplitContainer");
56 // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
57 // to keep other combined css classes from inadvertantly making the overflow visible
59 this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
62 // create the fake dragger
63 if(typeof this.sizerWidth == "object"){
64 try{ //FIXME: do this without a try/catch
65 this.sizerWidth = parseInt(this.sizerWidth.toString());
66 }catch(e){ this.sizerWidth = 7; }
68 var sizer = this.virtualSizer = dojo.doc.createElement('div');
69 sizer.style.position = 'relative';
71 // #1681: work around the dreaded 'quirky percentages in IE' layout bug
72 // If the splitcontainer's dimensions are specified in percentages, it
73 // will be resized when the virtualsizer is displayed in _showSizingLine
74 // (typically expanding its bounds unnecessarily). This happens because
75 // we use position: relative for .dijitSplitContainer.
76 // The workaround: instead of changing the display style attribute,
77 // switch to changing the zIndex (bring to front/move to back)
79 sizer.style.zIndex = 10;
80 sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
81 this.domNode.appendChild(sizer);
82 dojo.setSelectable(sizer, false);
86 delete this.virtualSizer;
87 dojo.forEach(this._ownconnects, dojo.disconnect);
88 this.inherited(arguments);
91 if(this._started){ return; }
93 dojo.forEach(this.getChildren(), function(child, i, children){
94 // attach the children and create the draggers
95 this._injectChild(child);
97 if(i < children.length-1){
103 this._restoreState();
106 this.inherited(arguments);
109 _injectChild: function(child){
110 child.domNode.style.position = "absolute";
111 dojo.addClass(child.domNode, "dijitSplitPane");
114 _addSizer: function(){
115 var i = this.sizers.length;
117 // TODO: use a template for this!!!
118 var sizer = this.sizers[i] = dojo.doc.createElement('div');
119 this.domNode.appendChild(sizer);
121 sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
124 var thumb = dojo.doc.createElement('div');
125 thumb.className = 'thumb';
126 sizer.appendChild(thumb);
128 // FIXME: are you serious? why aren't we using mover start/stop combo?
130 var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();
131 this.connect(sizer, "onmousedown", handler);
133 dojo.setSelectable(sizer, false);
136 removeChild: function(widget){
137 // summary: Remove sizer, but only if widget is really our child and
138 // we have at least one sizer to throw away
139 if(this.sizers.length){
140 var i=dojo.indexOf(this.getChildren(), widget)
142 if(i==this.sizers.length){
145 dojo._destroyElement(this.sizers[i]);
146 this.sizers.splice(i,1);
150 // Remove widget and repaint
151 this.inherited(arguments);
157 addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
158 // summary: Add a child widget to the container
159 // child: a widget to add
160 // insertIndex: postion in the "stack" to add the child widget
162 this.inherited("addChild",arguments);
165 // Do the stuff that startup() does for each widget
166 this._injectChild(child);
167 var children = this.getChildren();
168 if(children.length > 1){
172 // and then reposition (ie, shrink) every pane to make room for the new guy
179 // Do layout of panels
181 // base class defines this._contentBox on initial creation and also
183 this.paneWidth = this._contentBox.w;
184 this.paneHeight = this._contentBox.h;
186 var children = this.getChildren();
187 if(!children.length){ return; }
193 var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
194 if(children.length > 1){
195 space -= this.sizerWidth * (children.length - 1);
199 // calculate total of SizeShare values
202 dojo.forEach(children, function(child){
203 outOf += child.sizeShare;
207 // work out actual pixels per sizeshare unit
209 var pixPerUnit = space / outOf;
212 // set the SizeActual member of each pane
215 dojo.forEach(children.slice(0, children.length - 1), function(child){
216 var size = Math.round(pixPerUnit * child.sizeShare);
217 child.sizeActual = size;
221 children[children.length-1].sizeActual = space - totalSize;
224 // make sure the sizes are ok
229 // now loop, positioning each pane and letting children resize themselves
233 var size = children[0].sizeActual;
234 this._movePanel(children[0], pos, size);
235 children[0].position = pos;
238 // if we don't have any sizers, our layout method hasn't been called yet
239 // so bail until we are called..TODO: REVISIT: need to change the startup
240 // algorithm to guaranteed the ordering of calls to layout method
245 dojo.some(children.slice(1), function(child, i){
250 // first we position the sizing handle before this pane
251 this._moveSlider(this.sizers[i], pos, this.sizerWidth);
252 this.sizers[i].position = pos;
253 pos += this.sizerWidth;
255 size = child.sizeActual;
256 this._movePanel(child, pos, size);
257 child.position = pos;
262 _movePanel: function(panel, pos, size){
263 if(this.isHorizontal){
264 panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
265 panel.domNode.style.top = 0;
266 var box = {w: size, h: this.paneHeight};
270 dojo.marginBox(panel.domNode, box);
273 panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually
274 panel.domNode.style.top = pos + 'px';
275 var box = {w: this.paneWidth, h: size};
279 dojo.marginBox(panel.domNode, box);
284 _moveSlider: function(slider, pos, size){
285 if(this.isHorizontal){
286 slider.style.left = pos + 'px';
287 slider.style.top = 0;
288 dojo.marginBox(slider, { w: size, h: this.paneHeight });
290 slider.style.left = 0;
291 slider.style.top = pos + 'px';
292 dojo.marginBox(slider, { w: this.paneWidth, h: size });
296 _growPane: function(growth, pane){
298 if(pane.sizeActual > pane.sizeMin){
299 if((pane.sizeActual - pane.sizeMin) > growth){
301 // stick all the growth in this pane
302 pane.sizeActual = pane.sizeActual - growth;
305 // put as much growth in here as we can
306 growth -= pane.sizeActual - pane.sizeMin;
307 pane.sizeActual = pane.sizeMin;
314 _checkSizes: function(){
316 var totalMinSize = 0;
318 var children = this.getChildren();
320 dojo.forEach(children, function(child){
321 totalSize += child.sizeActual;
322 totalMinSize += child.sizeMin;
325 // only make adjustments if we have enough space for all the minimums
327 if(totalMinSize <= totalSize){
331 dojo.forEach(children, function(child){
332 if(child.sizeActual < child.sizeMin){
333 growth += child.sizeMin - child.sizeActual;
334 child.sizeActual = child.sizeMin;
339 var list = this.isDraggingLeft ? children.reverse() : children;
340 dojo.forEach(list, function(child){
341 growth = this._growPane(growth, child);
345 dojo.forEach(children, function(child){
346 child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
351 beginSizing: function(e, i){
352 var children = this.getChildren();
353 this.paneBefore = children[i];
354 this.paneAfter = children[i+1];
356 this.isSizing = true;
357 this.sizingSplitter = this.sizers[i];
360 this.cover = dojo.doc.createElement('div');
361 this.domNode.appendChild(this.cover);
362 var s = this.cover.style;
363 s.position = 'absolute';
370 this.cover.style.zIndex = 1;
372 this.sizingSplitter.style.zIndex = 2;
374 // TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.)
375 this.originPos = dojo.coords(children[0].domNode, true);
376 if(this.isHorizontal){
377 var client = (e.layerX ? e.layerX : e.offsetX);
378 var screen = e.pageX;
379 this.originPos = this.originPos.x;
381 var client = (e.layerY ? e.layerY : e.offsetY);
382 var screen = e.pageY;
383 this.originPos = this.originPos.y;
385 this.startPoint = this.lastPoint = screen;
386 this.screenToClientOffset = screen - client;
387 this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
389 if(!this.activeSizing){
390 this._showSizingLine();
394 // attach mouse events
396 this._ownconnects = [];
397 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing"));
398 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing"));
403 changeSizing: function(e){
404 if(!this.isSizing){ return; }
405 this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
407 if(this.activeSizing){
410 this._moveSizingLine();
415 endSizing: function(e){
416 if(!this.isSizing){ return; }
418 this.cover.style.zIndex = -1;
420 if(!this.activeSizing){
421 this._hideSizingLine();
426 this.isSizing = false;
429 this._saveState(this);
432 dojo.forEach(this._ownconnects,dojo.disconnect);
435 movePoint: function(){
437 // make sure lastPoint is a legal point to drag to
438 var p = this.lastPoint - this.screenToClientOffset;
440 var a = p - this.dragOffset;
441 a = this.legaliseSplitPoint(a);
442 p = a + this.dragOffset;
444 this.lastPoint = p + this.screenToClientOffset;
447 legaliseSplitPoint: function(a){
449 a += this.sizingSplitter.position;
451 this.isDraggingLeft = !!(a > 0);
453 if(!this.activeSizing){
454 var min = this.paneBefore.position + this.paneBefore.sizeMin;
459 var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
465 a -= this.sizingSplitter.position;
472 _updateSize: function(){
473 //FIXME: sometimes this.lastPoint is NaN
474 var pos = this.lastPoint - this.dragOffset - this.originPos;
476 var start_region = this.paneBefore.position;
477 var end_region = this.paneAfter.position + this.paneAfter.sizeActual;
479 this.paneBefore.sizeActual = pos - start_region;
480 this.paneAfter.position = pos + this.sizerWidth;
481 this.paneAfter.sizeActual = end_region - this.paneAfter.position;
483 dojo.forEach(this.getChildren(), function(child){
484 child.sizeShare = child.sizeActual;
492 _showSizingLine: function(){
494 this._moveSizingLine();
496 dojo.marginBox(this.virtualSizer,
497 this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
499 this.virtualSizer.style.display = 'block';
502 _hideSizingLine: function(){
503 this.virtualSizer.style.display = 'none';
506 _moveSizingLine: function(){
507 var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
508 dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
509 // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
512 _getCookieName: function(i){
513 return this.id + "_" + i;
516 _restoreState: function(){
517 dojo.forEach(this.getChildren(), function(child, i){
518 var cookieName = this._getCookieName(i);
519 var cookieValue = dojo.cookie(cookieName);
521 var pos = parseInt(cookieValue);
522 if(typeof pos == "number"){
523 child.sizeShare = pos;
529 _saveState: function(){
530 dojo.forEach(this.getChildren(), function(child, i){
531 dojo.cookie(this._getCookieName(i), child.sizeShare);
536 // These arguments can be specified for the children of a SplitContainer.
537 // Since any widget can be specified as a SplitContainer child, mix them
538 // into the base widget class. (This is a hack, but it's effective.)
539 dojo.extend(dijit._Widget, {
541 // Minimum size (width or height) of a child of a SplitContainer.
542 // The value is relative to other children's sizeShare properties.
545 // sizeShare: Integer
546 // Size (width or height) of a child of a SplitContainer.
547 // The value is relative to other children's sizeShare properties.
548 // For example, if there are two children and each has sizeShare=10, then
549 // each takes up 50% of the available space.