Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / Shadow.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.Shadow
17  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
18  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
19  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
20  */
21 Ext.define('Ext.Shadow', {
22     requires: ['Ext.ShadowPool'],
23
24     /**
25      * Creates new Shadow.
26      * @param {Object} config (optional) Config object.
27      */
28     constructor: function(config) {
29         var me = this,
30             adjusts = {
31                 h: 0
32             },
33             offset,
34             rad;
35         
36         Ext.apply(me, config);
37         if (!Ext.isString(me.mode)) {
38             me.mode = me.defaultMode;
39         }
40         offset = me.offset;
41         rad = Math.floor(offset / 2);
42         me.opacity = 50;
43         switch (me.mode.toLowerCase()) {
44             // all this hideous nonsense calculates the various offsets for shadows
45             case "drop":
46                 if (Ext.supports.CSS3BoxShadow) {
47                     adjusts.w = adjusts.h = -offset;
48                     adjusts.l = adjusts.t = offset;
49                 } else {
50                     adjusts.w = 0;
51                     adjusts.l = adjusts.t = offset;
52                     adjusts.t -= 1;
53                     if (Ext.isIE) {
54                         adjusts.l -= offset + rad;
55                         adjusts.t -= offset + rad;
56                         adjusts.w -= rad;
57                         adjusts.h -= rad;
58                         adjusts.t += 1;
59                     }
60                 }
61                 break;
62             case "sides":
63                 if (Ext.supports.CSS3BoxShadow) {
64                     adjusts.h -= offset;
65                     adjusts.t = offset;
66                     adjusts.l = adjusts.w = 0;
67                 } else {
68                     adjusts.w = (offset * 2);
69                     adjusts.l = -offset;
70                     adjusts.t = offset - 1;
71                     if (Ext.isIE) {
72                         adjusts.l -= (offset - rad);
73                         adjusts.t -= offset + rad;
74                         adjusts.l += 1;
75                         adjusts.w -= (offset - rad) * 2;
76                         adjusts.w -= rad + 1;
77                         adjusts.h -= 1;
78                     }
79                 }
80                 break;
81             case "frame":
82                 if (Ext.supports.CSS3BoxShadow) {
83                     adjusts.l = adjusts.w = adjusts.t = 0;
84                 } else {
85                     adjusts.w = adjusts.h = (offset * 2);
86                     adjusts.l = adjusts.t = -offset;
87                     adjusts.t += 1;
88                     adjusts.h -= 2;
89                     if (Ext.isIE) {
90                         adjusts.l -= (offset - rad);
91                         adjusts.t -= (offset - rad);
92                         adjusts.l += 1;
93                         adjusts.w -= (offset + rad + 1);
94                         adjusts.h -= (offset + rad);
95                         adjusts.h += 1;
96                     }
97                     break;
98                 }
99         }
100         me.adjusts = adjusts;
101     },
102
103     /**
104      * @cfg {String} mode
105      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
106      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
107      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
108      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
109      * </ul></div>
110      */
111     /**
112      * @cfg {Number} offset
113      * The number of pixels to offset the shadow from the element
114      */
115     offset: 4,
116
117     // private
118     defaultMode: "drop",
119
120     /**
121      * Displays the shadow under the target element
122      * @param {String/HTMLElement/Ext.Element} targetEl The id or element under which the shadow should display
123      */
124     show: function(target) {
125         var me = this,
126             index;
127         
128         target = Ext.get(target);
129         if (!me.el) {
130             me.el = Ext.ShadowPool.pull();
131             if (me.el.dom.nextSibling != target.dom) {
132                 me.el.insertBefore(target);
133             }
134         }
135         index = (parseInt(target.getStyle("z-index"), 10) - 1) || 0;
136         me.el.setStyle("z-index", me.zIndex || index);
137         if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
138             me.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=" + me.opacity + ") progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (me.offset) + ")";
139         }
140         me.realign(
141             target.getLeft(true),
142             target.getTop(true),
143             target.dom.offsetWidth,
144             target.dom.offsetHeight
145         );
146         me.el.dom.style.display = "block";
147     },
148
149     /**
150      * Returns true if the shadow is visible, else false
151      */
152     isVisible: function() {
153         return this.el ? true: false;
154     },
155
156     /**
157      * Direct alignment when values are already available. Show must be called at least once before
158      * calling this method to ensure it is initialized.
159      * @param {Number} left The target element left position
160      * @param {Number} top The target element top position
161      * @param {Number} width The target element width
162      * @param {Number} height The target element height
163      */
164     realign: function(l, t, targetWidth, targetHeight) {
165         if (!this.el) {
166             return;
167         }
168         var adjusts = this.adjusts,
169             d = this.el.dom,
170             targetStyle = d.style,
171             shadowWidth,
172             shadowHeight,
173             cn,
174             sww, 
175             sws, 
176             shs;
177
178         targetStyle.left = (l + adjusts.l) + "px";
179         targetStyle.top = (t + adjusts.t) + "px";
180         shadowWidth = Math.max(targetWidth + adjusts.w, 0);
181         shadowHeight = Math.max(targetHeight + adjusts.h, 0);
182         sws = shadowWidth + "px";
183         shs = shadowHeight + "px";
184         if (targetStyle.width != sws || targetStyle.height != shs) {
185             targetStyle.width = sws;
186             targetStyle.height = shs;
187             if (Ext.supports.CSS3BoxShadow) {
188                 targetStyle.boxShadow = '0 0 ' + this.offset + 'px 0 #888';
189             } else {
190
191                 // Adjust the 9 point framed element to poke out on the required sides
192                 if (!Ext.isIE) {
193                     cn = d.childNodes;
194                     sww = Math.max(0, (shadowWidth - 12)) + "px";
195                     cn[0].childNodes[1].style.width = sww;
196                     cn[1].childNodes[1].style.width = sww;
197                     cn[2].childNodes[1].style.width = sww;
198                     cn[1].style.height = Math.max(0, (shadowHeight - 12)) + "px";
199                 }
200             }
201         }
202     },
203
204     /**
205      * Hides this shadow
206      */
207     hide: function() {
208         var me = this;
209         
210         if (me.el) {
211             me.el.dom.style.display = "none";
212             Ext.ShadowPool.push(me.el);
213             delete me.el;
214         }
215     },
216
217     /**
218      * Adjust the z-index of this shadow
219      * @param {Number} zindex The new z-index
220      */
221     setZIndex: function(z) {
222         this.zIndex = z;
223         if (this.el) {
224             this.el.setStyle("z-index", z);
225         }
226     },
227     
228     /**
229      * Sets the opacity of the shadow
230      * @param {Number} opacity The opacity
231      */
232     setOpacity: function(opacity){
233         if (this.el) {
234             if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
235                 opacity = Math.floor(opacity * 100 / 2) / 100;
236             }
237             this.opacity = opacity;
238             this.el.setOpacity(opacity);
239         }
240     }
241 });