Fix svg element size
[mootools.git] / Source / Element / Element.Dimensions.js
blob0224ea78c2c39461330ba84a88d056709776e134
1 /*
2 ---
4 name: Element.Dimensions
6 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
8 license: MIT-style license.
10 credits:
11   - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
12   - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
14 requires: [Element, Element.Style]
16 provides: [Element.Dimensions]
18 ...
21 (function(){
23 var element = document.createElement('div'),
24         child = document.createElement('div');
25 element.style.height = '0';
26 element.appendChild(child);
27 var brokenOffsetParent = (child.offsetParent === element);
28 element = child = null;
30 var isOffset = function(el){
31         return styleString(el, 'position') != 'static' || isBody(el);
34 var isOffsetStatic = function(el){
35         return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
38 var computeSize = function(v){
39         return parseFloat(v.match(/([0-9.?]{1,})/));
42 Element.implement({
44         scrollTo: function(x, y){
45                 if (isBody(this)){
46                         this.getWindow().scrollTo(x, y);
47                 } else {
48                         this.scrollLeft = x;
49                         this.scrollTop = y;
50                 }
51                 return this;
52         },
54         getSize: function(){
55                 if (isBody(this)) return this.getWindow().getSize();
56                 
57                 // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with hidden visibility.
58                 if (!document.addEventListener) return {x: this.offsetWidth, y: this.offsetHeight};
59                 
60                 // This section inside the `if == true` can be removed when FF fixed the svg size bug.
61                 // The same applies to the computeSize function at L38 `var computeSize = function(v){`.
62                 if (this.get('tag') == 'svg'){
64                         var gCS = window.getComputedStyle(this), bounds;
65                         var gCScomponents = {
66                                 height: ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
67                                 width: ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth']
68                         };
69                         bounds = {x: 0, y: 0};
70                         gCScomponents.height.each(function(css){
71                                 bounds.y += computeSize(gCS[css]);
72                         });
73                         gCScomponents.width.each(function(css){
74                                 bounds.x += computeSize(gCS[css]);
75                         });
76                         return bounds;
78                 } else {
79                         bounds = this.getBoundingClientRect();
80                         return {x: bounds.width, y: bounds.height};
81                 }
82         },
84         getScrollSize: function(){
85                 if (isBody(this)) return this.getWindow().getScrollSize();
86                 return {x: this.scrollWidth, y: this.scrollHeight};
87         },
89         getScroll: function(){
90                 if (isBody(this)) return this.getWindow().getScroll();
91                 return {x: this.scrollLeft, y: this.scrollTop};
92         },
94         getScrolls: function(){
95                 var element = this.parentNode, position = {x: 0, y: 0};
96                 while (element && !isBody(element)){
97                         position.x += element.scrollLeft;
98                         position.y += element.scrollTop;
99                         element = element.parentNode;
100                 }
101                 return position;
102         },
104         getOffsetParent: brokenOffsetParent ? function(){
105                 var element = this;
106                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
108                 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
109                 while ((element = element.parentNode)){
110                         if (isOffsetCheck(element)) return element;
111                 }
112                 return null;
113         } : function(){
114                 var element = this;
115                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
117                 try {
118                         return element.offsetParent;
119                 } catch(e){}
120                 return null;
121         },
123         getOffsets: function(){
124                 var hasGetBoundingClientRect = this.getBoundingClientRect;
125 //<1.4compat>
126                 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
127 //</1.4compat>
128                 if (hasGetBoundingClientRect){
129                         var bound = this.getBoundingClientRect(),
130                                 html = document.id(this.getDocument().documentElement),
131                                 htmlScroll = html.getScroll(),
132                                 elemScrolls = this.getScrolls(),
133                                 isFixed = (styleString(this, 'position') == 'fixed');
135                         return {
136                                 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
137                                 y: bound.top.toInt()  + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
138                         };
139                 }
141                 var element = this, position = {x: 0, y: 0};
142                 if (isBody(this)) return position;
144                 while (element && !isBody(element)){
145                         position.x += element.offsetLeft;
146                         position.y += element.offsetTop;
147 //<1.4compat>
148                         if (Browser.firefox){
149                                 if (!borderBox(element)){
150                                         position.x += leftBorder(element);
151                                         position.y += topBorder(element);
152                                 }
153                                 var parent = element.parentNode;
154                                 if (parent && styleString(parent, 'overflow') != 'visible'){
155                                         position.x += leftBorder(parent);
156                                         position.y += topBorder(parent);
157                                 }
158                         } else if (element != this && Browser.safari){
159                                 position.x += leftBorder(element);
160                                 position.y += topBorder(element);
161                         }
162 //</1.4compat>
163                         element = element.offsetParent;
164                 }
165 //<1.4compat>
166                 if (Browser.firefox && !borderBox(this)){
167                         position.x -= leftBorder(this);
168                         position.y -= topBorder(this);
169                 }
170 //</1.4compat>
171                 return position;
172         },
174         getPosition: function(relative){
175                 var offset = this.getOffsets(),
176                         scroll = this.getScrolls();
177                 var position = {
178                         x: offset.x - scroll.x,
179                         y: offset.y - scroll.y
180                 };
182                 if (relative && (relative = document.id(relative))){
183                         var relativePosition = relative.getPosition();
184                         return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
185                 }
186                 return position;
187         },
189         getCoordinates: function(element){
190                 if (isBody(this)) return this.getWindow().getCoordinates();
191                 var position = this.getPosition(element),
192                         size = this.getSize();
193                 var obj = {
194                         left: position.x,
195                         top: position.y,
196                         width: size.x,
197                         height: size.y
198                 };
199                 obj.right = obj.left + obj.width;
200                 obj.bottom = obj.top + obj.height;
201                 return obj;
202         },
204         computePosition: function(obj){
205                 return {
206                         left: obj.x - styleNumber(this, 'margin-left'),
207                         top: obj.y - styleNumber(this, 'margin-top')
208                 };
209         },
211         setPosition: function(obj){
212                 return this.setStyles(this.computePosition(obj));
213         }
218 [Document, Window].invoke('implement', {
220         getSize: function(){
221                 var doc = getCompatElement(this);
222                 return {x: doc.clientWidth, y: doc.clientHeight};
223         },
225         getScroll: function(){
226                 var win = this.getWindow(), doc = getCompatElement(this);
227                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
228         },
230         getScrollSize: function(){
231                 var doc = getCompatElement(this),
232                         min = this.getSize(),
233                         body = this.getDocument().body;
235                 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
236         },
238         getPosition: function(){
239                 return {x: 0, y: 0};
240         },
242         getCoordinates: function(){
243                 var size = this.getSize();
244                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
245         }
249 // private methods
251 var styleString = Element.getComputedStyle;
253 function styleNumber(element, style){
254         return styleString(element, style).toInt() || 0;
257 function borderBox(element){
258         return styleString(element, '-moz-box-sizing') == 'border-box';
261 function topBorder(element){
262         return styleNumber(element, 'border-top-width');
265 function leftBorder(element){
266         return styleNumber(element, 'border-left-width');
269 function isBody(element){
270         return (/^(?:body|html)$/i).test(element.tagName);
273 function getCompatElement(element){
274         var doc = element.getDocument();
275         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
278 })();
280 //aliases
281 Element.alias({position: 'setPosition'}); //compatability
283 [Window, Document, Element].invoke('implement', {
285         getHeight: function(){
286                 return this.getSize().y;
287         },
289         getWidth: function(){
290                 return this.getSize().x;
291         },
293         getScrollTop: function(){
294                 return this.getScroll().y;
295         },
297         getScrollLeft: function(){
298                 return this.getScroll().x;
299         },
301         getScrollHeight: function(){
302                 return this.getScrollSize().y;
303         },
305         getScrollWidth: function(){
306                 return this.getScrollSize().x;
307         },
309         getTop: function(){
310                 return this.getPosition().y;
311         },
313         getLeft: function(){
314                 return this.getPosition().x;
315         }