Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / src / core / Element.style-more.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.Element\r
9  */\r
10 \r
11 // special markup used throughout Ext when box wrapping elements\r
12 Ext.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';\r
13 \r
14 Ext.Element.addMethods(function(){\r
15     var INTERNAL = "_internal",\r
16         pxMatch = /(\d+)px/;\r
17     return {\r
18         /**\r
19          * More flexible version of {@link #setStyle} for setting style properties.\r
20          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
21          * a function which returns such a specification.\r
22          * @return {Ext.Element} this\r
23          */\r
24         applyStyles : function(style){\r
25             Ext.DomHelper.applyStyles(this.dom, style);\r
26             return this;\r
27         },\r
28 \r
29         /**\r
30          * Returns an object with properties matching the styles requested.\r
31          * For example, el.getStyles('color', 'font-size', 'width') might return\r
32          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
33          * @param {String} style1 A style name\r
34          * @param {String} style2 A style name\r
35          * @param {String} etc.\r
36          * @return {Object} The style object\r
37          */\r
38         getStyles : function(){\r
39             var ret = {};\r
40             Ext.each(arguments, function(v) {\r
41                ret[v] = this.getStyle(v);\r
42             },\r
43             this);\r
44             return ret;\r
45         },\r
46 \r
47         // deprecated\r
48         getStyleSize : function(){\r
49             var me = this,\r
50                 w,\r
51                 h,\r
52                 d = this.dom,\r
53                 s = d.style;\r
54             if(s.width && s.width != 'auto'){\r
55                 w = parseInt(s.width, 10);\r
56                 if(me.isBorderBox()){\r
57                    w -= me.getFrameWidth('lr');\r
58                 }\r
59             }\r
60             if(s.height && s.height != 'auto'){\r
61                 h = parseInt(s.height, 10);\r
62                 if(me.isBorderBox()){\r
63                    h -= me.getFrameWidth('tb');\r
64                 }\r
65             }\r
66             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};\r
67         },\r
68 \r
69         // private  ==> used by ext full\r
70         setOverflow : function(v){\r
71             var dom = this.dom;\r
72             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
73                 dom.style.overflow = 'hidden';\r
74                 (function(){dom.style.overflow = 'auto';}).defer(1);\r
75             }else{\r
76                 dom.style.overflow = v;\r
77             }\r
78         },\r
79 \r
80        /**\r
81         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as\r
82         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>\r
83         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},\r
84         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup\r
85         * is of this form:</p>\r
86         * <pre><code>\r
87     Ext.Element.boxMarkup =\r
88     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>\r
89      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>\r
90      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;\r
91         * </code></pre>\r
92         * <p>Example usage:</p>\r
93         * <pre><code>\r
94     // Basic box wrap\r
95     Ext.get("foo").boxWrap();\r
96 \r
97     // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
98     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
99     // for how to create a custom box wrap style.\r
100     Ext.get("foo").boxWrap().addClass("x-box-blue");\r
101         * </code></pre>\r
102         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element\r
103         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on\r
104         * this name to make the overall effect work, so if you supply an alternate base class, make sure you\r
105         * also supply all of the necessary rules.\r
106         * @return {Ext.Element} The outermost wrapping element of the created box structure.\r
107         */\r
108         boxWrap : function(cls){\r
109             cls = cls || 'x-box';\r
110             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>"));        //String.format('<div class="{0}">'+Ext.Element.boxMarkup+'</div>', cls)));\r
111             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);\r
112             return el;\r
113         },\r
114 \r
115         /**\r
116          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.\r
117          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
118          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
119          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
120          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>\r
121          * </ul></div>\r
122          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
123          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
124          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
125          * </ul></div>\r
126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
127          * @return {Ext.Element} this\r
128          */\r
129         setSize : function(width, height, animate){\r
130             var me = this;\r
131             if(Ext.isObject(width)){ // in case of object from getSize()\r
132                 height = width.height;\r
133                 width = width.width;\r
134             }\r
135             width = me.adjustWidth(width);\r
136             height = me.adjustHeight(height);\r
137             if(!animate || !me.anim){\r
138                 me.dom.style.width = me.addUnits(width);\r
139                 me.dom.style.height = me.addUnits(height);\r
140             }else{\r
141                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));\r
142             }\r
143             return me;\r
144         },\r
145 \r
146         /**\r
147          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
148          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
149          * if a height has not been set using CSS.\r
150          * @return {Number}\r
151          */\r
152         getComputedHeight : function(){\r
153             var me = this,\r
154                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);\r
155             if(!h){\r
156                 h = parseInt(me.getStyle('height'), 10) || 0;\r
157                 if(!me.isBorderBox()){\r
158                     h += me.getFrameWidth('tb');\r
159                 }\r
160             }\r
161             return h;\r
162         },\r
163 \r
164         /**\r
165          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
166          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
167          * if a width has not been set using CSS.\r
168          * @return {Number}\r
169          */\r
170         getComputedWidth : function(){\r
171             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
172             if(!w){\r
173                 w = parseInt(this.getStyle('width'), 10) || 0;\r
174                 if(!this.isBorderBox()){\r
175                     w += this.getFrameWidth('lr');\r
176                 }\r
177             }\r
178             return w;\r
179         },\r
180 \r
181         /**\r
182          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
183          for more information about the sides.\r
184          * @param {String} sides\r
185          * @return {Number}\r
186          */\r
187         getFrameWidth : function(sides, onlyContentBox){\r
188             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
189         },\r
190 \r
191         /**\r
192          * Sets up event handlers to add and remove a css class when the mouse is over this element\r
193          * @param {String} className\r
194          * @return {Ext.Element} this\r
195          */\r
196         addClassOnOver : function(className){\r
197             this.hover(\r
198                 function(){\r
199                     Ext.fly(this, INTERNAL).addClass(className);\r
200                 },\r
201                 function(){\r
202                     Ext.fly(this, INTERNAL).removeClass(className);\r
203                 }\r
204             );\r
205             return this;\r
206         },\r
207 \r
208         /**\r
209          * Sets up event handlers to add and remove a css class when this element has the focus\r
210          * @param {String} className\r
211          * @return {Ext.Element} this\r
212          */\r
213         addClassOnFocus : function(className){\r
214             this.on("focus", function(){\r
215                 Ext.fly(this, INTERNAL).addClass(className);\r
216             }, this.dom);\r
217             this.on("blur", function(){\r
218                 Ext.fly(this, INTERNAL).removeClass(className);\r
219             }, this.dom);\r
220             return this;\r
221         },\r
222 \r
223         /**\r
224          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)\r
225          * @param {String} className\r
226          * @return {Ext.Element} this\r
227          */\r
228         addClassOnClick : function(className){\r
229             var dom = this.dom;\r
230             this.on("mousedown", function(){\r
231                 Ext.fly(dom, INTERNAL).addClass(className);\r
232                 var d = Ext.getDoc(),\r
233                     fn = function(){\r
234                         Ext.fly(dom, INTERNAL).removeClass(className);\r
235                         d.removeListener("mouseup", fn);\r
236                     };\r
237                 d.on("mouseup", fn);\r
238             });\r
239             return this;\r
240         },\r
241 \r
242         /**\r
243          * <p>Returns the dimensions of the element available to lay content out in.<p>\r
244          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>\r
245          * example:<pre><code>\r
246         var vpSize = Ext.getBody().getViewSize();\r
247 \r
248         // all Windows created afterwards will have a default value of 90% height and 95% width\r
249         Ext.Window.override({\r
250             width: vpSize.width * 0.9,\r
251             height: vpSize.height * 0.95\r
252         });\r
253         // To handle window resizing you would have to hook onto onWindowResize.\r
254         </code></pre>\r
255          * @param {Boolean} contentBox True to return the W3 content box <i>within</i> the padding area of the element. False\r
256          * or omitted to return the full area of the element within the border. See <a href="http://www.w3.org/TR/CSS2/box.html">http://www.w3.org/TR/CSS2/box.html</a>\r
257          * @return {Object} An object containing the elements's area: <code>{width: &lt;element width>, height: &lt;element height>}</code>\r
258          */\r
259         getViewSize : function(contentBox){\r
260             var doc = document,\r
261                 me = this,\r
262                 d = me.dom,\r
263                 extdom = Ext.lib.Dom,\r
264                 isDoc = (d == doc || d == doc.body),\r
265                 isBB, w, h, tbBorder = 0, lrBorder = 0,\r
266                 tbPadding = 0, lrPadding = 0;\r
267             if (isDoc) {\r
268                 return { width: extdom.getViewWidth(), height: extdom.getViewHeight() };\r
269             }\r
270             isBB = me.isBorderBox();\r
271             tbBorder = me.getBorderWidth('tb');\r
272             lrBorder = me.getBorderWidth('lr');\r
273             tbPadding = me.getPadding('tb');\r
274             lrPadding = me.getPadding('lr');\r
275 \r
276             // Width calcs\r
277             // Try the style first, then clientWidth, then offsetWidth\r
278             if (w = me.getStyle('width').match(pxMatch)){\r
279                 if ((w = parseInt(w[1], 10)) && isBB){\r
280                     // Style includes the padding and border if isBB\r
281                     w -= (lrBorder + lrPadding);\r
282                 }\r
283                 if (!contentBox){\r
284                     w += lrPadding;\r
285                 }\r
286             } else {\r
287                 if (!(w = d.clientWidth) && (w = d.offsetWidth)){\r
288                     w -= lrBorder;\r
289                 }\r
290                 if (w && contentBox){\r
291                     w -= lrPadding;\r
292                 }\r
293             }\r
294 \r
295             // Height calcs\r
296             // Try the style first, then clientHeight, then offsetHeight\r
297             if (h = me.getStyle('height').match(pxMatch)){\r
298                 if ((h = parseInt(h[1], 10)) && isBB){\r
299                     // Style includes the padding and border if isBB\r
300                     h -= (tbBorder + tbPadding);\r
301                 }\r
302                 if (!contentBox){\r
303                     h += tbPadding;\r
304                 }\r
305             } else {\r
306                 if (!(h = d.clientHeight) && (h = d.offsetHeight)){\r
307                     h -= tbBorder;\r
308                 }\r
309                 if (h && contentBox){\r
310                     h -= tbPadding;\r
311                 }\r
312             }\r
313 \r
314             return {\r
315                 width : w,\r
316                 height : h\r
317             };\r
318         },\r
319 \r
320         /**\r
321          * Returns the size of the element.\r
322          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
323          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
324          */\r
325         getSize : function(contentSize){\r
326             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
327         },\r
328 \r
329         /**\r
330          * Forces the browser to repaint this element\r
331          * @return {Ext.Element} this\r
332          */\r
333         repaint : function(){\r
334             var dom = this.dom;\r
335             this.addClass("x-repaint");\r
336             setTimeout(function(){\r
337                 Ext.fly(dom).removeClass("x-repaint");\r
338             }, 1);\r
339             return this;\r
340         },\r
341 \r
342         /**\r
343          * Disables text selection for this element (normalized across browsers)\r
344          * @return {Ext.Element} this\r
345          */\r
346         unselectable : function(){\r
347             this.dom.unselectable = "on";\r
348             return this.swallowEvent("selectstart", true).\r
349                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").\r
350                         addClass("x-unselectable");\r
351         },\r
352 \r
353         /**\r
354          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
355          * then it returns the calculated width of the sides (see getPadding)\r
356          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
357          * @return {Object/Number}\r
358          */\r
359         getMargins : function(side){\r
360             var me = this,\r
361                 key,\r
362                 hash = {t:"top", l:"left", r:"right", b: "bottom"},\r
363                 o = {};\r
364 \r
365             if (!side) {\r
366                 for (key in me.margins){\r
367                     o[hash[key]] = parseInt(me.getStyle(me.margins[key]), 10) || 0;\r
368                 }\r
369                 return o;\r
370             } else {\r
371                 return me.addStyles.call(me, side, me.margins);\r
372             }\r
373         }\r
374     };\r
375 }());\r