]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/layout/SplitContainer.js
remove call-by-reference to comply with php 5.4
[tt-rss.git] / lib / dijit / layout / SplitContainer.js
CommitLineData
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
8if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9dojo._hasResource["dijit.layout.SplitContainer"] = true;
2f01fe57
AD
10dojo.provide("dijit.layout.SplitContainer");
11dojo.require("dojo.cookie");
12dojo.require("dijit.layout._LayoutWidget");
81bea17a
AD
13
14
15//
16// FIXME: make it prettier
17// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
18//
19
20
21dojo.declare("dijit.layout.SplitContainer",
22 dijit.layout._LayoutWidget,
23 {
24 // summary:
25 // Deprecated. Use `dijit.layout.BorderContainer` instead.
26 // description:
27 // A Container widget with sizing handles in-between each child.
28 // Contains multiple children widgets, all of which are displayed side by side
29 // (either horizontally or vertically); there's a bar between each of the children,
30 // and you can adjust the relative size of each child by dragging the bars.
31 //
32 // You must specify a size (width and height) for the SplitContainer.
33 // tags:
34 // deprecated
35
36 constructor: function(){
37 dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
38 },
39
40 // activeSizing: Boolean
41 // If true, the children's size changes as you drag the bar;
42 // otherwise, the sizes don't change until you drop the bar (by mouse-up)
43 activeSizing: false,
44
45 // sizerWidth: Integer
46 // Size in pixels of the bar between each child
47 sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
48
49 // orientation: String
50 // either 'horizontal' or vertical; indicates whether the children are
51 // arranged side-by-side or up/down.
52 orientation: 'horizontal',
53
54 // persist: Boolean
55 // Save splitter positions in a cookie
56 persist: true,
57
58 baseClass: "dijitSplitContainer",
59
60 postMixInProperties: function(){
61 this.inherited("postMixInProperties",arguments);
62 this.isHorizontal = (this.orientation == 'horizontal');
63 },
64
65 postCreate: function(){
66 this.inherited(arguments);
67 this.sizers = [];
68
69 // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
70 // to keep other combined css classes from inadvertantly making the overflow visible
71 if(dojo.isMozilla){
72 this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
73 }
74
75 // create the fake dragger
76 if(typeof this.sizerWidth == "object"){
77 try{ //FIXME: do this without a try/catch
78 this.sizerWidth = parseInt(this.sizerWidth.toString());
79 }catch(e){ this.sizerWidth = 7; }
80 }
81 var sizer = dojo.doc.createElement('div');
82 this.virtualSizer = sizer;
83 sizer.style.position = 'relative';
84
85 // #1681: work around the dreaded 'quirky percentages in IE' layout bug
86 // If the splitcontainer's dimensions are specified in percentages, it
87 // will be resized when the virtualsizer is displayed in _showSizingLine
88 // (typically expanding its bounds unnecessarily). This happens because
89 // we use position: relative for .dijitSplitContainer.
90 // The workaround: instead of changing the display style attribute,
91 // switch to changing the zIndex (bring to front/move to back)
92
93 sizer.style.zIndex = 10;
94 sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
95 this.domNode.appendChild(sizer);
96 dojo.setSelectable(sizer, false);
97 },
98
99 destroy: function(){
100 delete this.virtualSizer;
101 dojo.forEach(this._ownconnects, dojo.disconnect);
102 this.inherited(arguments);
103 },
104 startup: function(){
105 if(this._started){ return; }
106
107 dojo.forEach(this.getChildren(), function(child, i, children){
108 // attach the children and create the draggers
109 this._setupChild(child);
110
111 if(i < children.length-1){
112 this._addSizer();
113 }
114 }, this);
115
116 if(this.persist){
117 this._restoreState();
118 }
119
120 this.inherited(arguments);
121 },
122
123 _setupChild: function(/*dijit._Widget*/ child){
124 this.inherited(arguments);
125 child.domNode.style.position = "absolute";
126 dojo.addClass(child.domNode, "dijitSplitPane");
127 },
128
129 _onSizerMouseDown: function(e){
130 if(e.target.id){
131 for(var i=0;i<this.sizers.length;i++){
132 if(this.sizers[i].id == e.target.id){
133 break;
134 }
135 }
136 if(i<this.sizers.length){
137 this.beginSizing(e,i);
138 }
139 }
140 },
141 _addSizer: function(index){
142 index = index === undefined ? this.sizers.length : index;
143
144 // TODO: use a template for this!!!
145 var sizer = dojo.doc.createElement('div');
146 sizer.id=dijit.getUniqueId('dijit_layout_SplitterContainer_Splitter');
147 this.sizers.splice(index,0,sizer);
148 this.domNode.appendChild(sizer);
149
150 sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
151
152 // add the thumb div
153 var thumb = dojo.doc.createElement('div');
154 thumb.className = 'thumb';
155 sizer.appendChild(thumb);
156
157 // FIXME: are you serious? why aren't we using mover start/stop combo?
158 this.connect(sizer, "onmousedown", '_onSizerMouseDown');
159
160 dojo.setSelectable(sizer, false);
161 },
162
163 removeChild: function(widget){
164 // summary:
165 // Remove sizer, but only if widget is really our child and
166 // we have at least one sizer to throw away
167 if(this.sizers.length){
168 var i=dojo.indexOf(this.getChildren(), widget)
169 if(i != -1){
170 if(i == this.sizers.length){
171 i--;
172 }
173 dojo.destroy(this.sizers[i]);
174 this.sizers.splice(i,1);
175 }
176 }
177
178 // Remove widget and repaint
179 this.inherited(arguments);
180 if(this._started){
181 this.layout();
182 }
183 },
184
185 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
186 // summary:
187 // Add a child widget to the container
188 // child:
189 // a widget to add
190 // insertIndex:
191 // postion in the "stack" to add the child widget
192
193 this.inherited(arguments);
194
195 if(this._started){
196 // Do the stuff that startup() does for each widget
197 var children = this.getChildren();
198 if(children.length > 1){
199 this._addSizer(insertIndex);
200 }
201
202 // and then reposition (ie, shrink) every pane to make room for the new guy
203 this.layout();
204 }
205 },
206
207 layout: function(){
208 // summary:
209 // Do layout of panels
210
211 // base class defines this._contentBox on initial creation and also
212 // on resize
213 this.paneWidth = this._contentBox.w;
214 this.paneHeight = this._contentBox.h;
215
216 var children = this.getChildren();
217 if(!children.length){ return; }
218
219 //
220 // calculate space
221 //
222
223 var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
224 if(children.length > 1){
225 space -= this.sizerWidth * (children.length - 1);
226 }
227
228 //
229 // calculate total of SizeShare values
230 //
231 var outOf = 0;
232 dojo.forEach(children, function(child){
233 outOf += child.sizeShare;
234 });
235
236 //
237 // work out actual pixels per sizeshare unit
238 //
239 var pixPerUnit = space / outOf;
240
241 //
242 // set the SizeActual member of each pane
243 //
244 var totalSize = 0;
245 dojo.forEach(children.slice(0, children.length - 1), function(child){
246 var size = Math.round(pixPerUnit * child.sizeShare);
247 child.sizeActual = size;
248 totalSize += size;
249 });
250
251 children[children.length-1].sizeActual = space - totalSize;
252
253 //
254 // make sure the sizes are ok
255 //
256 this._checkSizes();
257
258 //
259 // now loop, positioning each pane and letting children resize themselves
260 //
261
262 var pos = 0;
263 var size = children[0].sizeActual;
264 this._movePanel(children[0], pos, size);
265 children[0].position = pos;
266 pos += size;
267
268 // if we don't have any sizers, our layout method hasn't been called yet
269 // so bail until we are called..TODO: REVISIT: need to change the startup
270 // algorithm to guaranteed the ordering of calls to layout method
271 if(!this.sizers){
272 return;
273 }
274
275 dojo.some(children.slice(1), function(child, i){
276 // error-checking
277 if(!this.sizers[i]){
278 return true;
279 }
280 // first we position the sizing handle before this pane
281 this._moveSlider(this.sizers[i], pos, this.sizerWidth);
282 this.sizers[i].position = pos;
283 pos += this.sizerWidth;
284
285 size = child.sizeActual;
286 this._movePanel(child, pos, size);
287 child.position = pos;
288 pos += size;
289 }, this);
290 },
291
292 _movePanel: function(panel, pos, size){
293 if(this.isHorizontal){
294 panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
295 panel.domNode.style.top = 0;
296 var box = {w: size, h: this.paneHeight};
297 if(panel.resize){
298 panel.resize(box);
299 }else{
300 dojo.marginBox(panel.domNode, box);
301 }
302 }else{
303 panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually
304 panel.domNode.style.top = pos + 'px';
305 var box = {w: this.paneWidth, h: size};
306 if(panel.resize){
307 panel.resize(box);
308 }else{
309 dojo.marginBox(panel.domNode, box);
310 }
311 }
312 },
313
314 _moveSlider: function(slider, pos, size){
315 if(this.isHorizontal){
316 slider.style.left = pos + 'px';
317 slider.style.top = 0;
318 dojo.marginBox(slider, { w: size, h: this.paneHeight });
319 }else{
320 slider.style.left = 0;
321 slider.style.top = pos + 'px';
322 dojo.marginBox(slider, { w: this.paneWidth, h: size });
323 }
324 },
325
326 _growPane: function(growth, pane){
327 if(growth > 0){
328 if(pane.sizeActual > pane.sizeMin){
329 if((pane.sizeActual - pane.sizeMin) > growth){
330
331 // stick all the growth in this pane
332 pane.sizeActual = pane.sizeActual - growth;
333 growth = 0;
334 }else{
335 // put as much growth in here as we can
336 growth -= pane.sizeActual - pane.sizeMin;
337 pane.sizeActual = pane.sizeMin;
338 }
339 }
340 }
341 return growth;
342 },
343
344 _checkSizes: function(){
345
346 var totalMinSize = 0;
347 var totalSize = 0;
348 var children = this.getChildren();
349
350 dojo.forEach(children, function(child){
351 totalSize += child.sizeActual;
352 totalMinSize += child.sizeMin;
353 });
354
355 // only make adjustments if we have enough space for all the minimums
356
357 if(totalMinSize <= totalSize){
358
359 var growth = 0;
360
361 dojo.forEach(children, function(child){
362 if(child.sizeActual < child.sizeMin){
363 growth += child.sizeMin - child.sizeActual;
364 child.sizeActual = child.sizeMin;
365 }
366 });
367
368 if(growth > 0){
369 var list = this.isDraggingLeft ? children.reverse() : children;
370 dojo.forEach(list, function(child){
371 growth = this._growPane(growth, child);
372 }, this);
373 }
374 }else{
375 dojo.forEach(children, function(child){
376 child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
377 });
378 }
379 },
380
381 beginSizing: function(e, i){
382 var children = this.getChildren();
383 this.paneBefore = children[i];
384 this.paneAfter = children[i+1];
385
386 this.isSizing = true;
387 this.sizingSplitter = this.sizers[i];
388
389 if(!this.cover){
390 this.cover = dojo.create('div', {
391 style: {
392 position:'absolute',
393 zIndex:5,
394 top: 0,
395 left: 0,
396 width: "100%",
397 height: "100%"
398 }
399 }, this.domNode);
400 }else{
401 this.cover.style.zIndex = 5;
402 }
403 this.sizingSplitter.style.zIndex = 6;
404
405 // 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.)
406 this.originPos = dojo.position(children[0].domNode, true);
407 if(this.isHorizontal){
408 var client = e.layerX || e.offsetX || 0;
409 var screen = e.pageX;
410 this.originPos = this.originPos.x;
411 }else{
412 var client = e.layerY || e.offsetY || 0;
413 var screen = e.pageY;
414 this.originPos = this.originPos.y;
415 }
416 this.startPoint = this.lastPoint = screen;
417 this.screenToClientOffset = screen - client;
418 this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
419
420 if(!this.activeSizing){
421 this._showSizingLine();
422 }
423
424 //
425 // attach mouse events
426 //
427 this._ownconnects = [];
428 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing"));
429 this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing"));
430
431 dojo.stopEvent(e);
432 },
433
434 changeSizing: function(e){
435 if(!this.isSizing){ return; }
436 this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
437 this.movePoint();
438 if(this.activeSizing){
439 this._updateSize();
440 }else{
441 this._moveSizingLine();
442 }
443 dojo.stopEvent(e);
444 },
445
446 endSizing: function(e){
447 if(!this.isSizing){ return; }
448 if(this.cover){
449 this.cover.style.zIndex = -1;
450 }
451 if(!this.activeSizing){
452 this._hideSizingLine();
453 }
454
455 this._updateSize();
456
457 this.isSizing = false;
458
459 if(this.persist){
460 this._saveState(this);
461 }
462
463 dojo.forEach(this._ownconnects, dojo.disconnect);
464 },
465
466 movePoint: function(){
467
468 // make sure lastPoint is a legal point to drag to
469 var p = this.lastPoint - this.screenToClientOffset;
470
471 var a = p - this.dragOffset;
472 a = this.legaliseSplitPoint(a);
473 p = a + this.dragOffset;
474
475 this.lastPoint = p + this.screenToClientOffset;
476 },
477
478 legaliseSplitPoint: function(a){
479
480 a += this.sizingSplitter.position;
481
482 this.isDraggingLeft = !!(a > 0);
483
484 if(!this.activeSizing){
485 var min = this.paneBefore.position + this.paneBefore.sizeMin;
486 if(a < min){
487 a = min;
488 }
489
490 var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
491 if(a > max){
492 a = max;
493 }
494 }
495
496 a -= this.sizingSplitter.position;
497
498 this._checkSizes();
499
500 return a;
501 },
502
503 _updateSize: function(){
504 //FIXME: sometimes this.lastPoint is NaN
505 var pos = this.lastPoint - this.dragOffset - this.originPos;
506
507 var start_region = this.paneBefore.position;
508 var end_region = this.paneAfter.position + this.paneAfter.sizeActual;
509
510 this.paneBefore.sizeActual = pos - start_region;
511 this.paneAfter.position = pos + this.sizerWidth;
512 this.paneAfter.sizeActual = end_region - this.paneAfter.position;
513
514 dojo.forEach(this.getChildren(), function(child){
515 child.sizeShare = child.sizeActual;
516 });
517
518 if(this._started){
519 this.layout();
520 }
521 },
522
523 _showSizingLine: function(){
524
525 this._moveSizingLine();
526
527 dojo.marginBox(this.virtualSizer,
528 this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
529
530 this.virtualSizer.style.display = 'block';
531 },
532
533 _hideSizingLine: function(){
534 this.virtualSizer.style.display = 'none';
535 },
536
537 _moveSizingLine: function(){
538 var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
539 dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
540 // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
541 },
542
543 _getCookieName: function(i){
544 return this.id + "_" + i;
545 },
546
547 _restoreState: function(){
548 dojo.forEach(this.getChildren(), function(child, i){
549 var cookieName = this._getCookieName(i);
550 var cookieValue = dojo.cookie(cookieName);
551 if(cookieValue){
552 var pos = parseInt(cookieValue);
553 if(typeof pos == "number"){
554 child.sizeShare = pos;
555 }
556 }
557 }, this);
558 },
559
560 _saveState: function(){
561 if(!this.persist){
562 return;
563 }
564 dojo.forEach(this.getChildren(), function(child, i){
565 dojo.cookie(this._getCookieName(i), child.sizeShare, {expires:365});
566 }, this);
567 }
2f01fe57 568});
81bea17a
AD
569
570// These arguments can be specified for the children of a SplitContainer.
571// Since any widget can be specified as a SplitContainer child, mix them
572// into the base widget class. (This is a hack, but it's effective.)
573dojo.extend(dijit._Widget, {
574 // sizeMin: [deprecated] Integer
575 // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
576 // Minimum size (width or height) of a child of a SplitContainer.
577 // The value is relative to other children's sizeShare properties.
578 sizeMin: 10,
579
580 // sizeShare: [deprecated] Integer
581 // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
582 // Size (width or height) of a child of a SplitContainer.
583 // The value is relative to other children's sizeShare properties.
584 // For example, if there are two children and each has sizeShare=10, then
585 // each takes up 50% of the available space.
586 sizeShare: 10
2f01fe57 587});
81bea17a 588
2f01fe57 589}