initial import
[roojs1] / Roo / Layer.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  (function(){ 
12 /**
13  * @class Roo.Layer
14  * @extends Roo.Element
15  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16  * automatic maintaining of shadow/shim positions.
17  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
18  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
19  * you can pass a string with a CSS class name. False turns off the shadow.
20  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22  * @cfg {String} cls CSS class to add to the element
23  * @cfg {Number} zindex Starting z-index (defaults to 11000)
24  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25  * @constructor
26  * @param {Object} config An object with config options.
27  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
28  */
29
30 Roo.Layer = function(config, existingEl){
31     config = config || {};
32     var dh = Roo.DomHelper;
33     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
34     if(existingEl){
35         this.dom = Roo.getDom(existingEl);
36     }
37     if(!this.dom){
38         var o = config.dh || {tag: "div", cls: "x-layer"};
39         this.dom = dh.append(pel, o);
40     }
41     if(config.cls){
42         this.addClass(config.cls);
43     }
44     this.constrain = config.constrain !== false;
45     this.visibilityMode = Roo.Element.VISIBILITY;
46     if(config.id){
47         this.id = this.dom.id = config.id;
48     }else{
49         this.id = Roo.id(this.dom);
50     }
51     this.zindex = config.zindex || this.getZIndex();
52     this.position("absolute", this.zindex);
53     if(config.shadow){
54         this.shadowOffset = config.shadowOffset || 4;
55         this.shadow = new Roo.Shadow({
56             offset : this.shadowOffset,
57             mode : config.shadow
58         });
59     }else{
60         this.shadowOffset = 0;
61     }
62     this.useShim = config.shim !== false && Roo.useShims;
63     this.useDisplay = config.useDisplay;
64     this.hide();
65 };
66
67 var supr = Roo.Element.prototype;
68
69 // shims are shared among layer to keep from having 100 iframes
70 var shims = [];
71
72 Roo.extend(Roo.Layer, Roo.Element, {
73
74     getZIndex : function(){
75         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
76     },
77
78     getShim : function(){
79         if(!this.useShim){
80             return null;
81         }
82         if(this.shim){
83             return this.shim;
84         }
85         var shim = shims.shift();
86         if(!shim){
87             shim = this.createShim();
88             shim.enableDisplayMode('block');
89             shim.dom.style.display = 'none';
90             shim.dom.style.visibility = 'visible';
91         }
92         var pn = this.dom.parentNode;
93         if(shim.dom.parentNode != pn){
94             pn.insertBefore(shim.dom, this.dom);
95         }
96         shim.setStyle('z-index', this.getZIndex()-2);
97         this.shim = shim;
98         return shim;
99     },
100
101     hideShim : function(){
102         if(this.shim){
103             this.shim.setDisplayed(false);
104             shims.push(this.shim);
105             delete this.shim;
106         }
107     },
108
109     disableShadow : function(){
110         if(this.shadow){
111             this.shadowDisabled = true;
112             this.shadow.hide();
113             this.lastShadowOffset = this.shadowOffset;
114             this.shadowOffset = 0;
115         }
116     },
117
118     enableShadow : function(show){
119         if(this.shadow){
120             this.shadowDisabled = false;
121             this.shadowOffset = this.lastShadowOffset;
122             delete this.lastShadowOffset;
123             if(show){
124                 this.sync(true);
125             }
126         }
127     },
128
129     // private
130     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
131     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
132     sync : function(doShow){
133         var sw = this.shadow;
134         if(!this.updating && this.isVisible() && (sw || this.useShim)){
135             var sh = this.getShim();
136
137             var w = this.getWidth(),
138                 h = this.getHeight();
139
140             var l = this.getLeft(true),
141                 t = this.getTop(true);
142
143             if(sw && !this.shadowDisabled){
144                 if(doShow && !sw.isVisible()){
145                     sw.show(this);
146                 }else{
147                     sw.realign(l, t, w, h);
148                 }
149                 if(sh){
150                     if(doShow){
151                        sh.show();
152                     }
153                     // fit the shim behind the shadow, so it is shimmed too
154                     var a = sw.adjusts, s = sh.dom.style;
155                     s.left = (Math.min(l, l+a.l))+"px";
156                     s.top = (Math.min(t, t+a.t))+"px";
157                     s.width = (w+a.w)+"px";
158                     s.height = (h+a.h)+"px";
159                 }
160             }else if(sh){
161                 if(doShow){
162                    sh.show();
163                 }
164                 sh.setSize(w, h);
165                 sh.setLeftTop(l, t);
166             }
167             
168         }
169     },
170
171     // private
172     destroy : function(){
173         this.hideShim();
174         if(this.shadow){
175             this.shadow.hide();
176         }
177         this.removeAllListeners();
178         var pn = this.dom.parentNode;
179         if(pn){
180             pn.removeChild(this.dom);
181         }
182         Roo.Element.uncache(this.id);
183     },
184
185     remove : function(){
186         this.destroy();
187     },
188
189     // private
190     beginUpdate : function(){
191         this.updating = true;
192     },
193
194     // private
195     endUpdate : function(){
196         this.updating = false;
197         this.sync(true);
198     },
199
200     // private
201     hideUnders : function(negOffset){
202         if(this.shadow){
203             this.shadow.hide();
204         }
205         this.hideShim();
206     },
207
208     // private
209     constrainXY : function(){
210         if(this.constrain){
211             var vw = Roo.lib.Dom.getViewWidth(),
212                 vh = Roo.lib.Dom.getViewHeight();
213             var s = Roo.get(document).getScroll();
214
215             var xy = this.getXY();
216             var x = xy[0], y = xy[1];   
217             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
218             // only move it if it needs it
219             var moved = false;
220             // first validate right/bottom
221             if((x + w) > vw+s.left){
222                 x = vw - w - this.shadowOffset;
223                 moved = true;
224             }
225             if((y + h) > vh+s.top){
226                 y = vh - h - this.shadowOffset;
227                 moved = true;
228             }
229             // then make sure top/left isn't negative
230             if(x < s.left){
231                 x = s.left;
232                 moved = true;
233             }
234             if(y < s.top){
235                 y = s.top;
236                 moved = true;
237             }
238             if(moved){
239                 if(this.avoidY){
240                     var ay = this.avoidY;
241                     if(y <= ay && (y+h) >= ay){
242                         y = ay-h-5;   
243                     }
244                 }
245                 xy = [x, y];
246                 this.storeXY(xy);
247                 supr.setXY.call(this, xy);
248                 this.sync();
249             }
250         }
251     },
252
253     isVisible : function(){
254         return this.visible;    
255     },
256
257     // private
258     showAction : function(){
259         this.visible = true; // track visibility to prevent getStyle calls
260         if(this.useDisplay === true){
261             this.setDisplayed("");
262         }else if(this.lastXY){
263             supr.setXY.call(this, this.lastXY);
264         }else if(this.lastLT){
265             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
266         }
267     },
268
269     // private
270     hideAction : function(){
271         this.visible = false;
272         if(this.useDisplay === true){
273             this.setDisplayed(false);
274         }else{
275             this.setLeftTop(-10000,-10000);
276         }
277     },
278
279     // overridden Element method
280     setVisible : function(v, a, d, c, e){
281         if(v){
282             this.showAction();
283         }
284         if(a && v){
285             var cb = function(){
286                 this.sync(true);
287                 if(c){
288                     c();
289                 }
290             }.createDelegate(this);
291             supr.setVisible.call(this, true, true, d, cb, e);
292         }else{
293             if(!v){
294                 this.hideUnders(true);
295             }
296             var cb = c;
297             if(a){
298                 cb = function(){
299                     this.hideAction();
300                     if(c){
301                         c();
302                     }
303                 }.createDelegate(this);
304             }
305             supr.setVisible.call(this, v, a, d, cb, e);
306             if(v){
307                 this.sync(true);
308             }else if(!a){
309                 this.hideAction();
310             }
311         }
312     },
313
314     storeXY : function(xy){
315         delete this.lastLT;
316         this.lastXY = xy;
317     },
318
319     storeLeftTop : function(left, top){
320         delete this.lastXY;
321         this.lastLT = [left, top];
322     },
323
324     // private
325     beforeFx : function(){
326         this.beforeAction();
327         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
328     },
329
330     // private
331     afterFx : function(){
332         Roo.Layer.superclass.afterFx.apply(this, arguments);
333         this.sync(this.isVisible());
334     },
335
336     // private
337     beforeAction : function(){
338         if(!this.updating && this.shadow){
339             this.shadow.hide();
340         }
341     },
342
343     // overridden Element method
344     setLeft : function(left){
345         this.storeLeftTop(left, this.getTop(true));
346         supr.setLeft.apply(this, arguments);
347         this.sync();
348     },
349
350     setTop : function(top){
351         this.storeLeftTop(this.getLeft(true), top);
352         supr.setTop.apply(this, arguments);
353         this.sync();
354     },
355
356     setLeftTop : function(left, top){
357         this.storeLeftTop(left, top);
358         supr.setLeftTop.apply(this, arguments);
359         this.sync();
360     },
361
362     setXY : function(xy, a, d, c, e){
363         this.fixDisplay();
364         this.beforeAction();
365         this.storeXY(xy);
366         var cb = this.createCB(c);
367         supr.setXY.call(this, xy, a, d, cb, e);
368         if(!a){
369             cb();
370         }
371     },
372
373     // private
374     createCB : function(c){
375         var el = this;
376         return function(){
377             el.constrainXY();
378             el.sync(true);
379             if(c){
380                 c();
381             }
382         };
383     },
384
385     // overridden Element method
386     setX : function(x, a, d, c, e){
387         this.setXY([x, this.getY()], a, d, c, e);
388     },
389
390     // overridden Element method
391     setY : function(y, a, d, c, e){
392         this.setXY([this.getX(), y], a, d, c, e);
393     },
394
395     // overridden Element method
396     setSize : function(w, h, a, d, c, e){
397         this.beforeAction();
398         var cb = this.createCB(c);
399         supr.setSize.call(this, w, h, a, d, cb, e);
400         if(!a){
401             cb();
402         }
403     },
404
405     // overridden Element method
406     setWidth : function(w, a, d, c, e){
407         this.beforeAction();
408         var cb = this.createCB(c);
409         supr.setWidth.call(this, w, a, d, cb, e);
410         if(!a){
411             cb();
412         }
413     },
414
415     // overridden Element method
416     setHeight : function(h, a, d, c, e){
417         this.beforeAction();
418         var cb = this.createCB(c);
419         supr.setHeight.call(this, h, a, d, cb, e);
420         if(!a){
421             cb();
422         }
423     },
424
425     // overridden Element method
426     setBounds : function(x, y, w, h, a, d, c, e){
427         this.beforeAction();
428         var cb = this.createCB(c);
429         if(!a){
430             this.storeXY([x, y]);
431             supr.setXY.call(this, [x, y]);
432             supr.setSize.call(this, w, h, a, d, cb, e);
433             cb();
434         }else{
435             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
436         }
437         return this;
438     },
439     
440     /**
441      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
442      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
443      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
444      * @param {Number} zindex The new z-index to set
445      * @return {this} The Layer
446      */
447     setZIndex : function(zindex){
448         this.zindex = zindex;
449         this.setStyle("z-index", zindex + 2);
450         if(this.shadow){
451             this.shadow.setZIndex(zindex + 1);
452         }
453         if(this.shim){
454             this.shim.setStyle("z-index", zindex);
455         }
456     }
457 });
458 })();