/*
Script: Element.Dimensions.js
Contains methods to work with size, scroll, or positioning of Elements and the window object.

License:
MIT-style license.

Credits:
- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
*/

(function(){

Element.implement({

scrollTo: function(x, y){
if (isBody(this)){
this.getWindow().scrollTo(x, y);
} else {
this.scrollLeft = x;
this.scrollTop = y;
}
return this;
},

getSize: function(){
if (isBody(this)) return this.getWindow().getSize();
return {x: this.offsetWidth, y: this.offsetHeight};
},

getScrollSize: function(){
if (isBody(this)) return this.getWindow().getScrollSize();
return {x: this.scrollWidth, y: this.scrollHeight};
},

getScroll: function(){
if (isBody(this)) return this.getWindow().getScroll();
return {x: this.scrollLeft, y: this.scrollTop};
},

getScrolls: function(){
var element = this, position = {x: 0, y: 0};
while (element && !isBody(element)){
position.x += element.scrollLeft;
position.y += element.scrollTop;
element = element.parentNode;
}
return position;
},

getOffsetParent: function(){
var element = this;
if (isBody(element)) return null;
if (!Browser.Engine.trident) return element.offsetParent;
while ((element = element.parentNode) && !isBody(element)){
if (styleString(element, 'position') != 'static') return element;
}
return null;
},

getOffsets: function(){
var element = this, position = {x: 0, y: 0};
if (isBody(this)) return position;

while (element && !isBody(element)){
position.x += element.offsetLeft;
position.y += element.offsetTop;
if (Browser.Engine.gecko){
if (!borderBox(element)){
position.x += leftBorder(element);
position.y += topBorder(element);
}
var parent = element.parentNode;
if (parent && styleString(parent, 'overflow') != 'visible'){
position.x += leftBorder(parent);
position.y += topBorder(parent);
}
} else if (element != this && (Browser.Engine.trident || Browser.Engine.webkit)){
position.x += leftBorder(element);
position.y += topBorder(element);
}

element = element.offsetParent;
if (Browser.Engine.trident){
while (element && !element.currentStyle.hasLayout) element = element.offsetParent;
}
}
if (Browser.Engine.gecko && !borderBox(this)){
position.x -= leftBorder(this);
position.y -= topBorder(this);
}
return position;
},

getPosition: function(relative){
if (isBody(this)) return {x: 0, y: 0};
var offset = this.getOffsets(), scroll = this.getScrolls();
var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
},

getCoordinates: function(element){
if (isBody(this)) return this.getWindow().getCoordinates();
var position = this.getPosition(element), size = this.getSize();
var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
obj.right = obj.left + obj.width;
obj.bottom = obj.top + obj.height;
return obj;
},

computePosition: function(obj){
return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
},

position: function(obj){
return this.setStyles(this.computePosition(obj));
}

});

Native.implement([Document, Window], {

getSize: function(){
var win = this.getWindow();
if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight};
var doc = getCompatElement(this);
return {x: doc.clientWidth, y: doc.clientHeight};
},

getScroll: function(){
var win = this.getWindow();
var doc = getCompatElement(this);
return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
},

getScrollSize: function(){
var doc = getCompatElement(this);
var min = this.getSize();
return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
},

getPosition: function(){
return {x: 0, y: 0};
},

getCoordinates: function(){
var size = this.getSize();
return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
}

});

// private methods

var styleString = Element.getComputedStyle;

function styleNumber(element, style){
return styleString(element, style).toInt() || 0;
};

function borderBox(element){
return styleString(element, '-moz-box-sizing') == 'border-box';
};

function topBorder(element){
return styleNumber(element, 'border-top-width');
};

function leftBorder(element){
return styleNumber(element, 'border-left-width');
};

function isBody(element){
return (/^(?:body|html)$/i).test(element.tagName);
};

function getCompatElement(element){
var doc = element.getDocument();
return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
};

})();

//aliases

Native.implement([Window, Document, Element], {

getHeight: function(){
return this.getSize().y;
},

getWidth: function(){
return this.getSize().x;
},

getScrollTop: function(){
return this.getScroll().y;
},

getScrollLeft: function(){
return this.getScroll().x;
},

getScrollHeight: function(){
return this.getScrollSize().y;
},

getScrollWidth: function(){
return this.getScrollSize().x;
},

getTop: function(){
return this.getPosition().y;
},

getLeft: function(){
return this.getPosition().x;
}

});