initial import
[roojs1] / Roo / SplitBar.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.SplitBar
15  * @extends Roo.util.Observable
16  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
17  * <br><br>
18  * Usage:
19  * <pre><code>
20 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
21                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23 split.minSize = 100;
24 split.maxSize = 600;
25 split.animate = true;
26 split.on('moved', splitterMoved);
27 </code></pre>
28  * @constructor
29  * Create a new SplitBar
30  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
34                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35                         position of the SplitBar).
36  */
37 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
38     
39     /** @private */
40     this.el = Roo.get(dragElement, true);
41     this.el.dom.unselectable = "on";
42     /** @private */
43     this.resizingEl = Roo.get(resizingElement, true);
44
45     /**
46      * @private
47      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
48      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
49      * @type Number
50      */
51     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
52     
53     /**
54      * The minimum size of the resizing element. (Defaults to 0)
55      * @type Number
56      */
57     this.minSize = 0;
58     
59     /**
60      * The maximum size of the resizing element. (Defaults to 2000)
61      * @type Number
62      */
63     this.maxSize = 2000;
64     
65     /**
66      * Whether to animate the transition to the new size
67      * @type Boolean
68      */
69     this.animate = false;
70     
71     /**
72      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
73      * @type Boolean
74      */
75     this.useShim = false;
76     
77     /** @private */
78     this.shim = null;
79     
80     if(!existingProxy){
81         /** @private */
82         this.proxy = Roo.SplitBar.createProxy(this.orientation);
83     }else{
84         this.proxy = Roo.get(existingProxy).dom;
85     }
86     /** @private */
87     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
88     
89     /** @private */
90     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
91     
92     /** @private */
93     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
94     
95     /** @private */
96     this.dragSpecs = {};
97     
98     /**
99      * @private The adapter to use to positon and resize elements
100      */
101     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
102     this.adapter.init(this);
103     
104     if(this.orientation == Roo.SplitBar.HORIZONTAL){
105         /** @private */
106         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
107         this.el.addClass("x-splitbar-h");
108     }else{
109         /** @private */
110         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
111         this.el.addClass("x-splitbar-v");
112     }
113     
114     this.addEvents({
115         /**
116          * @event resize
117          * Fires when the splitter is moved (alias for {@link #event-moved})
118          * @param {Roo.SplitBar} this
119          * @param {Number} newSize the new width or height
120          */
121         "resize" : true,
122         /**
123          * @event moved
124          * Fires when the splitter is moved
125          * @param {Roo.SplitBar} this
126          * @param {Number} newSize the new width or height
127          */
128         "moved" : true,
129         /**
130          * @event beforeresize
131          * Fires before the splitter is dragged
132          * @param {Roo.SplitBar} this
133          */
134         "beforeresize" : true,
135
136         "beforeapply" : true
137     });
138
139     Roo.util.Observable.call(this);
140 };
141
142 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
143     onStartProxyDrag : function(x, y){
144         this.fireEvent("beforeresize", this);
145         if(!this.overlay){
146             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
147             o.unselectable();
148             o.enableDisplayMode("block");
149             // all splitbars share the same overlay
150             Roo.SplitBar.prototype.overlay = o;
151         }
152         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
153         this.overlay.show();
154         Roo.get(this.proxy).setDisplayed("block");
155         var size = this.adapter.getElementSize(this);
156         this.activeMinSize = this.getMinimumSize();;
157         this.activeMaxSize = this.getMaximumSize();;
158         var c1 = size - this.activeMinSize;
159         var c2 = Math.max(this.activeMaxSize - size, 0);
160         if(this.orientation == Roo.SplitBar.HORIZONTAL){
161             this.dd.resetConstraints();
162             this.dd.setXConstraint(
163                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
164                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
165             );
166             this.dd.setYConstraint(0, 0);
167         }else{
168             this.dd.resetConstraints();
169             this.dd.setXConstraint(0, 0);
170             this.dd.setYConstraint(
171                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
172                 this.placement == Roo.SplitBar.TOP ? c2 : c1
173             );
174          }
175         this.dragSpecs.startSize = size;
176         this.dragSpecs.startPoint = [x, y];
177         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
178     },
179     
180     /** 
181      * @private Called after the drag operation by the DDProxy
182      */
183     onEndProxyDrag : function(e){
184         Roo.get(this.proxy).setDisplayed(false);
185         var endPoint = Roo.lib.Event.getXY(e);
186         if(this.overlay){
187             this.overlay.hide();
188         }
189         var newSize;
190         if(this.orientation == Roo.SplitBar.HORIZONTAL){
191             newSize = this.dragSpecs.startSize + 
192                 (this.placement == Roo.SplitBar.LEFT ?
193                     endPoint[0] - this.dragSpecs.startPoint[0] :
194                     this.dragSpecs.startPoint[0] - endPoint[0]
195                 );
196         }else{
197             newSize = this.dragSpecs.startSize + 
198                 (this.placement == Roo.SplitBar.TOP ?
199                     endPoint[1] - this.dragSpecs.startPoint[1] :
200                     this.dragSpecs.startPoint[1] - endPoint[1]
201                 );
202         }
203         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
204         if(newSize != this.dragSpecs.startSize){
205             if(this.fireEvent('beforeapply', this, newSize) !== false){
206                 this.adapter.setElementSize(this, newSize);
207                 this.fireEvent("moved", this, newSize);
208                 this.fireEvent("resize", this, newSize);
209             }
210         }
211     },
212     
213     /**
214      * Get the adapter this SplitBar uses
215      * @return The adapter object
216      */
217     getAdapter : function(){
218         return this.adapter;
219     },
220     
221     /**
222      * Set the adapter this SplitBar uses
223      * @param {Object} adapter A SplitBar adapter object
224      */
225     setAdapter : function(adapter){
226         this.adapter = adapter;
227         this.adapter.init(this);
228     },
229     
230     /**
231      * Gets the minimum size for the resizing element
232      * @return {Number} The minimum size
233      */
234     getMinimumSize : function(){
235         return this.minSize;
236     },
237     
238     /**
239      * Sets the minimum size for the resizing element
240      * @param {Number} minSize The minimum size
241      */
242     setMinimumSize : function(minSize){
243         this.minSize = minSize;
244     },
245     
246     /**
247      * Gets the maximum size for the resizing element
248      * @return {Number} The maximum size
249      */
250     getMaximumSize : function(){
251         return this.maxSize;
252     },
253     
254     /**
255      * Sets the maximum size for the resizing element
256      * @param {Number} maxSize The maximum size
257      */
258     setMaximumSize : function(maxSize){
259         this.maxSize = maxSize;
260     },
261     
262     /**
263      * Sets the initialize size for the resizing element
264      * @param {Number} size The initial size
265      */
266     setCurrentSize : function(size){
267         var oldAnimate = this.animate;
268         this.animate = false;
269         this.adapter.setElementSize(this, size);
270         this.animate = oldAnimate;
271     },
272     
273     /**
274      * Destroy this splitbar. 
275      * @param {Boolean} removeEl True to remove the element
276      */
277     destroy : function(removeEl){
278         if(this.shim){
279             this.shim.remove();
280         }
281         this.dd.unreg();
282         this.proxy.parentNode.removeChild(this.proxy);
283         if(removeEl){
284             this.el.remove();
285         }
286     }
287 });
288
289 /**
290  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
291  */
292 Roo.SplitBar.createProxy = function(dir){
293     var proxy = new Roo.Element(document.createElement("div"));
294     proxy.unselectable();
295     var cls = 'x-splitbar-proxy';
296     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
297     document.body.appendChild(proxy.dom);
298     return proxy.dom;
299 };
300
301 /** 
302  * @class Roo.SplitBar.BasicLayoutAdapter
303  * Default Adapter. It assumes the splitter and resizing element are not positioned
304  * elements and only gets/sets the width of the element. Generally used for table based layouts.
305  */
306 Roo.SplitBar.BasicLayoutAdapter = function(){
307 };
308
309 Roo.SplitBar.BasicLayoutAdapter.prototype = {
310     // do nothing for now
311     init : function(s){
312     
313     },
314     /**
315      * Called before drag operations to get the current size of the resizing element. 
316      * @param {Roo.SplitBar} s The SplitBar using this adapter
317      */
318      getElementSize : function(s){
319         if(s.orientation == Roo.SplitBar.HORIZONTAL){
320             return s.resizingEl.getWidth();
321         }else{
322             return s.resizingEl.getHeight();
323         }
324     },
325     
326     /**
327      * Called after drag operations to set the size of the resizing element.
328      * @param {Roo.SplitBar} s The SplitBar using this adapter
329      * @param {Number} newSize The new size to set
330      * @param {Function} onComplete A function to be invoked when resizing is complete
331      */
332     setElementSize : function(s, newSize, onComplete){
333         if(s.orientation == Roo.SplitBar.HORIZONTAL){
334             if(!s.animate){
335                 s.resizingEl.setWidth(newSize);
336                 if(onComplete){
337                     onComplete(s, newSize);
338                 }
339             }else{
340                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
341             }
342         }else{
343             
344             if(!s.animate){
345                 s.resizingEl.setHeight(newSize);
346                 if(onComplete){
347                     onComplete(s, newSize);
348                 }
349             }else{
350                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
351             }
352         }
353     }
354 };
355
356 /** 
357  *@class Roo.SplitBar.AbsoluteLayoutAdapter
358  * @extends Roo.SplitBar.BasicLayoutAdapter
359  * Adapter that  moves the splitter element to align with the resized sizing element. 
360  * Used with an absolute positioned SplitBar.
361  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
362  * document.body, make sure you assign an id to the body element.
363  */
364 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
365     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
366     this.container = Roo.get(container);
367 };
368
369 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
370     init : function(s){
371         this.basic.init(s);
372     },
373     
374     getElementSize : function(s){
375         return this.basic.getElementSize(s);
376     },
377     
378     setElementSize : function(s, newSize, onComplete){
379         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
380     },
381     
382     moveSplitter : function(s){
383         var yes = Roo.SplitBar;
384         switch(s.placement){
385             case yes.LEFT:
386                 s.el.setX(s.resizingEl.getRight());
387                 break;
388             case yes.RIGHT:
389                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
390                 break;
391             case yes.TOP:
392                 s.el.setY(s.resizingEl.getBottom());
393                 break;
394             case yes.BOTTOM:
395                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
396                 break;
397         }
398     }
399 };
400
401 /**
402  * Orientation constant - Create a vertical SplitBar
403  * @static
404  * @type Number
405  */
406 Roo.SplitBar.VERTICAL = 1;
407
408 /**
409  * Orientation constant - Create a horizontal SplitBar
410  * @static
411  * @type Number
412  */
413 Roo.SplitBar.HORIZONTAL = 2;
414
415 /**
416  * Placement constant - The resizing element is to the left of the splitter element
417  * @static
418  * @type Number
419  */
420 Roo.SplitBar.LEFT = 1;
421
422 /**
423  * Placement constant - The resizing element is to the right of the splitter element
424  * @static
425  * @type Number
426  */
427 Roo.SplitBar.RIGHT = 2;
428
429 /**
430  * Placement constant - The resizing element is positioned above the splitter element
431  * @static
432  * @type Number
433  */
434 Roo.SplitBar.TOP = 3;
435
436 /**
437  * Placement constant - The resizing element is positioned under splitter element
438  * @static
439  * @type Number
440  */
441 Roo.SplitBar.BOTTOM = 4;