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