]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/layout/SplitContainer.js.uncompressed.js
1 define("dijit/layout/SplitContainer", [
2 "dojo/_base/array", // array.forEach array.indexOf array.some
3 "dojo/cookie", // cookie
4 "dojo/_base/declare", // declare
5 "dojo/dom", // dom.setSelectable
6 "dojo/dom-class", // domClass.add
7 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
8 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
9 "dojo/dom-style", // domStyle.style
10 "dojo/_base/event", // event.stop
11 "dojo/_base/kernel", // kernel.deprecated
12 "dojo/_base/lang", // lang.extend lang.hitch
14 "dojo/sniff", // has("mozilla")
15 "../registry", // registry.getUniqueId()
18 ], function(array
, cookie
, declare
, dom
, domClass
, domConstruct
, domGeometry
, domStyle
,
19 event
, kernel
, lang
, on
, has
, registry
, _WidgetBase
, _LayoutWidget
){
22 // dijit/layout/SplitContainer
25 // FIXME: make it prettier
26 // FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
27 // FIXME: sizeWidth should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
30 var SplitContainer
= declare("dijit.layout.SplitContainer", _LayoutWidget
, {
32 // Deprecated. Use `dijit/layout/BorderContainer` instead.
34 // A Container widget with sizing handles in-between each child.
35 // Contains multiple children widgets, all of which are displayed side by side
36 // (either horizontally or vertically); there's a bar between each of the children,
37 // and you can adjust the relative size of each child by dragging the bars.
39 // You must specify a size (width and height) for the SplitContainer.
41 // See `SplitContainer.ChildWidgetProperties` for details on the properties that can be set on
42 // children of a `SplitContainer`.
46 constructor: function(){
47 kernel
.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
50 // activeSizing: Boolean
51 // If true, the children's size changes as you drag the bar;
52 // otherwise, the sizes don't change until you drop the bar (by mouse-up)
55 // sizerWidth: Integer
56 // Size in pixels of the bar between each child
59 // orientation: String
60 // either 'horizontal' or vertical; indicates whether the children are
61 // arranged side-by-side or up/down.
62 orientation
: 'horizontal',
65 // Save splitter positions in a cookie
68 baseClass
: "dijitSplitContainer",
70 postMixInProperties: function(){
71 this.inherited("postMixInProperties",arguments
);
72 this.isHorizontal
= (this.orientation
== 'horizontal');
75 postCreate: function(){
76 this.inherited(arguments
);
79 // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
80 // to keep other combined css classes from inadvertantly making the overflow visible
82 this.domNode
.style
.overflow
= '-moz-scrollbars-none'; // hidden doesn't work
85 // create the fake dragger
86 if(typeof this.sizerWidth
== "object"){
87 try{ //FIXME: do this without a try/catch
88 this.sizerWidth
= parseInt(this.sizerWidth
.toString());
89 }catch(e
){ this.sizerWidth
= 7; }
91 var sizer
= this.ownerDocument
.createElement('div');
92 this.virtualSizer
= sizer
;
93 sizer
.style
.position
= 'relative';
95 // #1681: work around the dreaded 'quirky percentages in IE' layout bug
96 // If the splitcontainer's dimensions are specified in percentages, it
97 // will be resized when the virtualsizer is displayed in _showSizingLine
98 // (typically expanding its bounds unnecessarily). This happens because
99 // we use position: relative for .dijitSplitContainer.
100 // The workaround: instead of changing the display style attribute,
101 // switch to changing the zIndex (bring to front/move to back)
103 sizer
.style
.zIndex
= 10;
104 sizer
.className
= this.isHorizontal
? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
105 this.domNode
.appendChild(sizer
);
106 dom
.setSelectable(sizer
, false);
110 delete this.virtualSizer
;
111 if(this._ownconnects
){
113 while(h
= this._ownconnects
.pop()){ h
.remove(); }
115 this.inherited(arguments
);
118 if(this._started
){ return; }
120 array
.forEach(this.getChildren(), function(child
, i
, children
){
121 // attach the children and create the draggers
122 this._setupChild(child
);
124 if(i
< children
.length
-1){
130 this._restoreState();
133 this.inherited(arguments
);
136 _setupChild: function(/*dijit/_WidgetBase*/ child){
137 this.inherited(arguments);
138 child.domNode.style.position = "absolute";
139 domClass.add(child.domNode, "dijitSplitPane");
142 _onSizerMouseDown: function(e){
144 for(var i=0;i<this.sizers.length;i++){
145 if(this.sizers[i].id == e.target.id){
149 if(i<this.sizers.length){
150 this.beginSizing(e,i);
154 _addSizer: function(index){
155 index = index === undefined ? this.sizers.length : index;
157 // TODO: use a template for this!!!
158 var sizer = this.ownerDocument.createElement('div');
159 sizer.id=registry.getUniqueId('dijit_layout_SplitterContainer_Splitter');
160 this.sizers.splice(index,0,sizer);
161 this.domNode.appendChild(sizer);
163 sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
166 var thumb = this.ownerDocument.createElement('div');
167 thumb.className = 'thumb';
168 sizer.appendChild(thumb);
170 // FIXME: are you serious? why aren't we using mover start/stop combo?
171 this.connect(sizer, "onmousedown", '_onSizerMouseDown');
173 dom.setSelectable(sizer, false);
176 removeChild: function(widget){
178 // Remove sizer, but only if widget is really our child and
179 // we have at least one sizer to throw away
180 if(this.sizers.length){
181 var i = array.indexOf(this.getChildren(), widget);
183 if(i == this.sizers.length){
186 domConstruct.destroy(this.sizers[i]);
187 this.sizers.splice(i,1);
191 // Remove widget and repaint
192 this.inherited(arguments);
198 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
200 // Add a child widget to the container
204 // postion in the "stack" to add the child widget
206 this.inherited(arguments
);
209 // Do the stuff that startup() does for each widget
210 var children
= this.getChildren();
211 if(children
.length
> 1){
212 this._addSizer(insertIndex
);
215 // and then reposition (ie, shrink) every pane to make room for the new guy
222 // Do layout of panels
224 // base class defines this._contentBox on initial creation and also
226 this.paneWidth
= this._contentBox
.w
;
227 this.paneHeight
= this._contentBox
.h
;
229 var children
= this.getChildren();
230 if(!children
.length
){ return; }
236 var space
= this.isHorizontal
? this.paneWidth
: this.paneHeight
;
237 if(children
.length
> 1){
238 space
-= this.sizerWidth
* (children
.length
- 1);
242 // calculate total of SizeShare values
245 array
.forEach(children
, function(child
){
246 outOf
+= child
.sizeShare
;
250 // work out actual pixels per sizeshare unit
252 var pixPerUnit
= space
/ outOf
;
255 // set the SizeActual member of each pane
258 array
.forEach(children
.slice(0, children
.length
- 1), function(child
){
259 var size
= Math
.round(pixPerUnit
* child
.sizeShare
);
260 child
.sizeActual
= size
;
264 children
[children
.length
-1].sizeActual
= space
- totalSize
;
267 // make sure the sizes are ok
272 // now loop, positioning each pane and letting children resize themselves
276 var size
= children
[0].sizeActual
;
277 this._movePanel(children
[0], pos
, size
);
278 children
[0].position
= pos
;
281 // if we don't have any sizers, our layout method hasn't been called yet
282 // so bail until we are called..TODO: REVISIT: need to change the startup
283 // algorithm to guaranteed the ordering of calls to layout method
288 array
.some(children
.slice(1), function(child
, i
){
293 // first we position the sizing handle before this pane
294 this._moveSlider(this.sizers
[i
], pos
, this.sizerWidth
);
295 this.sizers
[i
].position
= pos
;
296 pos
+= this.sizerWidth
;
298 size
= child
.sizeActual
;
299 this._movePanel(child
, pos
, size
);
300 child
.position
= pos
;
305 _movePanel: function(panel
, pos
, size
){
307 if(this.isHorizontal
){
308 panel
.domNode
.style
.left
= pos
+ 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
309 panel
.domNode
.style
.top
= 0;
310 box
= {w
: size
, h
: this.paneHeight
};
314 domGeometry
.setMarginBox(panel
.domNode
, box
);
317 panel
.domNode
.style
.left
= 0; // TODO: resize() takes l and t parameters too, don't need to set manually
318 panel
.domNode
.style
.top
= pos
+ 'px';
319 box
= {w
: this.paneWidth
, h
: size
};
323 domGeometry
.setMarginBox(panel
.domNode
, box
);
328 _moveSlider: function(slider
, pos
, size
){
329 if(this.isHorizontal
){
330 slider
.style
.left
= pos
+ 'px';
331 slider
.style
.top
= 0;
332 domGeometry
.setMarginBox(slider
, { w
: size
, h
: this.paneHeight
});
334 slider
.style
.left
= 0;
335 slider
.style
.top
= pos
+ 'px';
336 domGeometry
.setMarginBox(slider
, { w
: this.paneWidth
, h
: size
});
340 _growPane: function(growth
, pane
){
342 if(pane
.sizeActual
> pane
.sizeMin
){
343 if((pane
.sizeActual
- pane
.sizeMin
) > growth
){
345 // stick all the growth in this pane
346 pane
.sizeActual
= pane
.sizeActual
- growth
;
349 // put as much growth in here as we can
350 growth
-= pane
.sizeActual
- pane
.sizeMin
;
351 pane
.sizeActual
= pane
.sizeMin
;
358 _checkSizes: function(){
360 var totalMinSize
= 0;
362 var children
= this.getChildren();
364 array
.forEach(children
, function(child
){
365 totalSize
+= child
.sizeActual
;
366 totalMinSize
+= child
.sizeMin
;
369 // only make adjustments if we have enough space for all the minimums
371 if(totalMinSize
<= totalSize
){
375 array
.forEach(children
, function(child
){
376 if(child
.sizeActual
< child
.sizeMin
){
377 growth
+= child
.sizeMin
- child
.sizeActual
;
378 child
.sizeActual
= child
.sizeMin
;
383 var list
= this.isDraggingLeft
? children
.reverse() : children
;
384 array
.forEach(list
, function(child
){
385 growth
= this._growPane(growth
, child
);
389 array
.forEach(children
, function(child
){
390 child
.sizeActual
= Math
.round(totalSize
* (child
.sizeMin
/ totalMinSize
));
395 beginSizing: function(e
, i
){
397 // Begin dragging the splitter between child[i] and child[i+1]
399 var children
= this.getChildren();
401 this.paneBefore
= children
[i
];
402 this.paneAfter
= children
[i
+1];
404 this.paneBefore
.sizeBeforeDrag
= this.paneBefore
.sizeActual
;
405 this.paneAfter
.sizeBeforeDrag
= this.paneAfter
.sizeActual
;
406 this.paneAfter
.positionBeforeDrag
= this.paneAfter
.position
;
408 this.isSizing
= true;
409 this.sizingSplitter
= this.sizers
[i
];
410 this.sizingSplitter
.positionBeforeDrag
= domStyle
.get(this.sizingSplitter
,(this.isHorizontal
? "left" : "top"));
413 this.cover
= domConstruct
.create('div', {
424 this.cover
.style
.zIndex
= 5;
426 this.sizingSplitter
.style
.zIndex
= 6;
428 // startPoint is the e.pageX or e.pageY at start of drag
429 this.startPoint
= this.lastPoint
= (this.isHorizontal
? e
.pageX
: e
.pageY
);
431 // Calculate maximum to the left or right that splitter is allowed to be dragged
432 // minDelta is negative to indicate left/upward drag where end.pageX < start.pageX.
433 this.maxDelta
= this.paneAfter
.sizeActual
- this.paneAfter
.sizeMin
;
434 this.minDelta
= -1 * (this.paneBefore
.sizeActual
- this.paneBefore
.sizeMin
);
436 if(!this.activeSizing
){
437 this._showSizingLine();
440 // attach mouse events
441 this._ownconnects
= [
442 on(this.ownerDocument
.documentElement
, "mousemove", lang
.hitch(this, "changeSizing")),
443 on(this.ownerDocument
.documentElement
, "mouseup", lang
.hitch(this, "endSizing"))
449 changeSizing: function(e
){
451 // Called on mousemove while dragging the splitter
453 if(!this.isSizing
){ return; }
455 // lastPoint is the most recent e.pageX or e.pageY during the drag
456 this.lastPoint
= this.isHorizontal
? e
.pageX
: e
.pageY
;
457 var delta
= Math
.max(Math
.min(this.lastPoint
- this.startPoint
, this.maxDelta
), this.minDelta
);
459 if(this.activeSizing
){
460 this._updateSize(delta
);
462 this._moveSizingLine(delta
);
467 endSizing: function(){
468 if(!this.isSizing
){ return; }
470 this.cover
.style
.zIndex
= -1;
472 if(!this.activeSizing
){
473 this._hideSizingLine();
476 var delta
= Math
.max(Math
.min(this.lastPoint
- this.startPoint
, this.maxDelta
), this.minDelta
);
477 this._updateSize(delta
);
479 this.isSizing
= false;
482 this._saveState(this);
486 while(h
= this._ownconnects
.pop()){ h
.remove(); }
489 _updateSize: function(/*Number*/ delta
){
491 // Resets sizes of panes before and after splitter being dragged.
492 // Called during a drag, for active sizing, or at the end of a drag otherwise.
494 // Change in slider position compared to start of drag. But note that
495 // this function may be called multiple times during drag.
497 this.paneBefore
.sizeActual
= this.paneBefore
.sizeBeforeDrag
+ delta
;
498 this.paneAfter
.position
= this.paneAfter
.positionBeforeDrag
+ delta
;
499 this.paneAfter
.sizeActual
= this.paneAfter
.sizeBeforeDrag
- delta
;
501 array
.forEach(this.getChildren(), function(child
){
502 child
.sizeShare
= child
.sizeActual
;
510 _showSizingLine: function(){
512 // Show virtual splitter, for non-active resizing
514 this._moveSizingLine(0);
516 domGeometry
.setMarginBox(this.virtualSizer
,
517 this.isHorizontal
? { w
: this.sizerWidth
, h
: this.paneHeight
} : { w
: this.paneWidth
, h
: this.sizerWidth
});
519 this.virtualSizer
.style
.display
= 'block';
522 _hideSizingLine: function(){
523 this.virtualSizer
.style
.display
= 'none';
526 _moveSizingLine: function(/*Number*/ delta
){
528 // Called for non-active resizing, to move the virtual splitter without adjusting the size of the panes
529 var pos
= delta
+ this.sizingSplitter
.positionBeforeDrag
;
530 domStyle
.set(this.virtualSizer
,(this.isHorizontal
? "left" : "top"),pos
+"px");
533 _getCookieName: function(i
){
534 return this.id
+ "_" + i
;
537 _restoreState: function(){
538 array
.forEach(this.getChildren(), function(child
, i
){
539 var cookieName
= this._getCookieName(i
);
540 var cookieValue
= cookie(cookieName
);
542 var pos
= parseInt(cookieValue
);
543 if(typeof pos
== "number"){
544 child
.sizeShare
= pos
;
550 _saveState: function(){
554 array
.forEach(this.getChildren(), function(child
, i
){
555 cookie(this._getCookieName(i
), child
.sizeShare
, {expires
:365});
560 SplitContainer
.ChildWidgetProperties
= {
562 // These properties can be specified for the children of a SplitContainer.
564 // sizeMin: [deprecated] Integer
565 // Minimum size (width or height) of a child of a SplitContainer.
566 // The value is relative to other children's sizeShare properties.
569 // sizeShare: [deprecated] Integer
570 // Size (width or height) of a child of a SplitContainer.
571 // The value is relative to other children's sizeShare properties.
572 // For example, if there are two children and each has sizeShare=10, then
573 // each takes up 50% of the available space.
577 // Since any widget can be specified as a SplitContainer child, mix them
578 // into the base widget class. (This is a hack, but it's effective.)
579 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
580 lang
.extend(_WidgetBase
, /*===== {} || =====*/ SplitContainer
.ChildWidgetProperties
);
582 return SplitContainer
;