Upgrade to ExtJS 4.0.2 - Released 06/09/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.core.Element
17  */
18 (function(){
19     var doc = document,
20         activeElement = null,
21         isCSS1 = doc.compatMode == "CSS1Compat",
22         ELEMENT = Ext.core.Element,
23         fly = function(el){
24             if (!_fly) {
25                 _fly = new Ext.core.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         getXY : function(el) {
162             var p,
163                 pe,
164                 b,
165                 bt,
166                 bl,
167                 dbd,
168                 x = 0,
169                 y = 0,
170                 scroll,
171                 hasAbsolute,
172                 bd = (doc.body || doc.documentElement),
173                 ret = [0,0];
174
175             el = Ext.getDom(el);
176
177             if(el != bd){
178                 hasAbsolute = fly(el).isStyle("position", "absolute");
179
180                 if (el.getBoundingClientRect) {
181                     b = el.getBoundingClientRect();
182                     scroll = fly(document).getScroll();
183                     ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
184                 } else {
185                     p = el;
186
187                     while (p) {
188                         pe = fly(p);
189                         x += p.offsetLeft;
190                         y += p.offsetTop;
191
192                         hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
193
194                         if (Ext.isGecko) {
195                             y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
196                             x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
197
198                             if (p != el && !pe.isStyle('overflow','visible')) {
199                                 x += bl;
200                                 y += bt;
201                             }
202                         }
203                         p = p.offsetParent;
204                     }
205
206                     if (Ext.isSafari && hasAbsolute) {
207                         x -= bd.offsetLeft;
208                         y -= bd.offsetTop;
209                     }
210
211                     if (Ext.isGecko && !hasAbsolute) {
212                         dbd = fly(bd);
213                         x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
214                         y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
215                     }
216
217                     p = el.parentNode;
218                     while (p && p != bd) {
219                         if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
220                             x -= p.scrollLeft;
221                             y -= p.scrollTop;
222                         }
223                         p = p.parentNode;
224                     }
225                     ret = [x,y];
226                 }
227             }
228             return ret;
229         },
230
231         setXY : function(el, xy) {
232             (el = Ext.fly(el, '_setXY')).position();
233
234             var pts = el.translatePoints(xy),
235                 style = el.dom.style,
236                 pos;
237
238             for (pos in pts) {
239                 if (!isNaN(pts[pos])) {
240                     style[pos] = pts[pos] + "px";
241                 }
242             }
243         },
244
245         setX : function(el, x) {
246             ELEMENT.setXY(el, [x, false]);
247         },
248
249         setY : function(el, y) {
250             ELEMENT.setXY(el, [false, y]);
251         },
252
253         /**
254          * Serializes a DOM form into a url encoded string
255          * @param {Object} form The form
256          * @return {String} The url encoded form
257          */
258         serializeForm: function(form) {
259             var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
260                 hasSubmit = false,
261                 encoder = encodeURIComponent,
262                 name,
263                 data = '',
264                 type,
265                 hasValue;
266
267             Ext.each(fElements, function(element){
268                 name = element.name;
269                 type = element.type;
270
271                 if (!element.disabled && name) {
272                     if (/select-(one|multiple)/i.test(type)) {
273                         Ext.each(element.options, function(opt){
274                             if (opt.selected) {
275                                 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
276                                 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
277                             }
278                         });
279                     } else if (!(/file|undefined|reset|button/i.test(type))) {
280                         if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
281                             data += encoder(name) + '=' + encoder(element.value) + '&';
282                             hasSubmit = /submit/i.test(type);
283                         }
284                     }
285                 }
286             });
287             return data.substr(0, data.length - 1);
288         }
289     });
290 })();
291