Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / core / src / dom / Element.static-more.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.Element
17  */
18 (function(){
19     var doc = document,
20         activeElement = null,
21         isCSS1 = doc.compatMode == "CSS1Compat",
22         ELEMENT = Ext.Element,
23         fly = function(el){
24             if (!_fly) {
25                 _fly = new Ext.Element.Flyweight();
26             }
27             _fly.dom = el;
28             return _fly;
29         }, _fly;
30
31     // If the browser does not support document.activeElement we need some assistance.
32     // This covers old Safari 3.2 (4.0 added activeElement along with just about all
33     // other browsers). We need this support to handle issues with old Safari.
34     if (!('activeElement' in doc) && doc.addEventListener) {
35         doc.addEventListener('focus',
36             function (ev) {
37                 if (ev && ev.target) {
38                     activeElement = (ev.target == doc) ? null : ev.target;
39                 }
40             }, true);
41     }
42
43     /*
44      * Helper function to create the function that will restore the selection.
45      */
46     function makeSelectionRestoreFn (activeEl, start, end) {
47         return function () {
48             activeEl.selectionStart = start;
49             activeEl.selectionEnd = end;
50         };
51     }
52
53     Ext.apply(ELEMENT, {
54         isAncestor : function(p, c) {
55             var ret = false;
56
57             p = Ext.getDom(p);
58             c = Ext.getDom(c);
59             if (p && c) {
60                 if (p.contains) {
61                     return p.contains(c);
62                 } else if (p.compareDocumentPosition) {
63                     return !!(p.compareDocumentPosition(c) & 16);
64                 } else {
65                     while ((c = c.parentNode)) {
66                         ret = c == p || ret;
67                     }
68                 }
69             }
70             return ret;
71         },
72
73         /**
74          * Returns the active element in the DOM. If the browser supports activeElement
75          * on the document, this is returned. If not, the focus is tracked and the active
76          * element is maintained internally.
77          * @return {HTMLElement} The active (focused) element in the document.
78          */
79         getActiveElement: function () {
80             return doc.activeElement || activeElement;
81         },
82
83         /**
84          * Creates a function to call to clean up problems with the work-around for the
85          * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
86          * the element before calling getComputedStyle and then to restore its original
87          * display value. The problem with this is that it corrupts the selection of an
88          * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
89          * To cleanup after this, we need to capture the selection of any such element and
90          * then restore it after we have restored the display style.
91          *
92          * @param target {Element} The top-most element being adjusted.
93          * @private
94          */
95         getRightMarginFixCleaner: function (target) {
96             var supports = Ext.supports,
97                 hasInputBug = supports.DisplayChangeInputSelectionBug,
98                 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
99
100             if (hasInputBug || hasTextAreaBug) {
101                 var activeEl = doc.activeElement || activeElement, // save a call
102                     tag = activeEl && activeEl.tagName,
103                     start,
104                     end;
105
106                 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
107                     (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
108                     if (ELEMENT.isAncestor(target, activeEl)) {
109                         start = activeEl.selectionStart;
110                         end = activeEl.selectionEnd;
111
112                         if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
113                             // We don't create the raw closure here inline because that
114                             // will be costly even if we don't want to return it (nested
115                             // function decls and exprs are often instantiated on entry
116                             // regardless of whether execution ever reaches them):
117                             return makeSelectionRestoreFn(activeEl, start, end);
118                         }
119                     }
120                 }
121             }
122
123             return Ext.emptyFn; // avoid special cases, just return a nop
124         },
125
126         getViewWidth : function(full) {
127             return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
128         },
129
130         getViewHeight : function(full) {
131             return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
132         },
133
134         getDocumentHeight: function() {
135             return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
136         },
137
138         getDocumentWidth: function() {
139             return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
140         },
141
142         getViewportHeight: function(){
143             return Ext.isIE ?
144                    (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
145                    self.innerHeight;
146         },
147
148         getViewportWidth : function() {
149             return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
150                    Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
151         },
152
153         getY : function(el) {
154             return ELEMENT.getXY(el)[1];
155         },
156
157         getX : function(el) {
158             return ELEMENT.getXY(el)[0];
159         },
160
161         getOffsetParent: function (el) {
162             el = Ext.getDom(el);
163             try {
164                 // accessing offsetParent can throw "Unspecified Error" in IE6-8 (not 9)
165                 return el.offsetParent;
166             } catch (e) {
167                 var body = document.body; // safe bet, unless...
168                 return (el == body) ? null : body;
169             }
170         },
171
172         getXY : function(el) {
173             var p,
174                 pe,
175                 b,
176                 bt,
177                 bl,
178                 dbd,
179                 x = 0,
180                 y = 0,
181                 scroll,
182                 hasAbsolute,
183                 bd = (doc.body || doc.documentElement),
184                 ret;
185
186             el = Ext.getDom(el);
187
188             if(el != bd){
189                 hasAbsolute = fly(el).isStyle("position", "absolute");
190
191                 if (el.getBoundingClientRect) {
192                     try {
193                         b = el.getBoundingClientRect();
194                         scroll = fly(document).getScroll();
195                         ret = [ Math.round(b.left + scroll.left), Math.round(b.top + scroll.top) ];
196                     } catch (e) {
197                         // IE6-8 can also throw from getBoundingClientRect...
198                     }
199                 }
200
201                 if (!ret) {
202                     for (p = el; p; p = ELEMENT.getOffsetParent(p)) {
203                         pe = fly(p);
204                         x += p.offsetLeft;
205                         y += p.offsetTop;
206
207                         hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
208
209                         if (Ext.isGecko) {
210                             y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
211                             x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
212
213                             if (p != el && !pe.isStyle('overflow','visible')) {
214                                 x += bl;
215                                 y += bt;
216                             }
217                         }
218                     }
219
220                     if (Ext.isSafari && hasAbsolute) {
221                         x -= bd.offsetLeft;
222                         y -= bd.offsetTop;
223                     }
224
225                     if (Ext.isGecko && !hasAbsolute) {
226                         dbd = fly(bd);
227                         x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
228                         y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
229                     }
230
231                     p = el.parentNode;
232                     while (p && p != bd) {
233                         if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
234                             x -= p.scrollLeft;
235                             y -= p.scrollTop;
236                         }
237                         p = p.parentNode;
238                     }
239                     ret = [x,y];
240                 }
241             }
242             return ret || [0,0];
243         },
244
245         setXY : function(el, xy) {
246             (el = Ext.fly(el, '_setXY')).position();
247
248             var pts = el.translatePoints(xy),
249                 style = el.dom.style,
250                 pos;
251
252             for (pos in pts) {
253                 if (!isNaN(pts[pos])) {
254                     style[pos] = pts[pos] + "px";
255                 }
256             }
257         },
258
259         setX : function(el, x) {
260             ELEMENT.setXY(el, [x, false]);
261         },
262
263         setY : function(el, y) {
264             ELEMENT.setXY(el, [false, y]);
265         },
266
267         /**
268          * Serializes a DOM form into a url encoded string
269          * @param {Object} form The form
270          * @return {String} The url encoded form
271          */
272         serializeForm: function(form) {
273             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
274                 hasSubmit = false,
275                 encoder = encodeURIComponent,
276                 name,
277                 data = '',
278                 type,
279                 hasValue;
280
281             Ext.each(fElements, function(element){
282                 name = element.name;
283                 type = element.type;
284
285                 if (!element.disabled && name) {
286                     if (/select-(one|multiple)/i.test(type)) {
287                         Ext.each(element.options, function(opt){
288                             if (opt.selected) {
289                                 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
290                                 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
291                             }
292                         });
293                     } else if (!(/file|undefined|reset|button/i.test(type))) {
294                         if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
295                             data += encoder(name) + '=' + encoder(element.value) + '&';
296                             hasSubmit = /submit/i.test(type);
297                         }
298                     }
299                 }
300             });
301             return data.substr(0, data.length - 1);
302         }
303     });
304 })();
305