/**
* This plugin overrides jQuery's height() and width() functions and
* adds more handy stuff for cross-browser compatibility.
*/

/**
* Returns the css height value for the first matched element.
* If used on document, returns the document's height (innerHeight)
* If used on window, returns the viewport's (window) height
*
* @example $("#testdiv").height()
* @result "200px"
*
* @example $(document).height();
* @result 800
*
* @example $(window).height();
* @result 400
*
* @name height
* @type Object
* @cat Plugins/Dimensions
*/
jQuery.fn.height = function() {
       if ( this.get(0) == window )
               return self.innerHeight ||
                       jQuery.boxModel && document.documentElement.clientHeight ||
                       document.body.clientHeight;
       
       if ( this.get(0) == document )
               return Math.max( document.body.scrollHeight, document.body.offsetHeight );
       
       return this.css("height", arguments[0]);
};

/**
* Returns the css width value for the first matched element.
* If used on document, returns the document's width (innerWidth)
* If used on window, returns the viewport's (window) width
*
* @example $("#testdiv").width()
* @result "200px"
*
* @example $(document).width();
* @result 800
*
* @example $(window).width();
* @result 400
*
* @name width
* @type Object
* @cat Plugins/Dimensions
*/
jQuery.fn.width = function() {
       if ( this.get(0) == window )
               return self.innerWidth ||
                       jQuery.boxModel && document.documentElement.clientWidth ||
                       document.body.clientWidth;
       
       if ( this.get(0) == document )
               return Math.max( document.body.scrollWidth, document.body.offsetWidth );
       
       return this.css("width", arguments[0]);
};

/**
* Returns the inner height value (without border) for the first matched element.
* If used on document, returns the document's height (innerHeight)
* If used on window, returns the viewport's (window) height
*
* @example $("#testdiv").innerHeight()
* @result 800
*
* @name innerHeight
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.innerHeight = function() {
       return this.get(0) == window || this.get(0) == document ?
               this.height() :
               this.get(0).offsetHeight - parseInt(this.css("borderTop") || 0) - parseInt(this.css("borderBottom") || 0);
};

/**
* Returns the inner width value (without border) for the first matched element.
* If used on document, returns the document's Width (innerWidth)
* If used on window, returns the viewport's (window) width
*
* @example $("#testdiv").innerWidth()
* @result 1000
*
* @name innerWidth
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.innerWidth = function() {
       return this.get(0) == window || this.get(0) == document ?
               this.width() :
               this.get(0).offsetWidth - parseInt(this.css("borderLeft") || 0) - parseInt(this.css("borderRight") || 0);
};

/**
* Returns the outer height value (including border) for the first matched element.
* Cannot be used on document or window.
*
* @example $("#testdiv").outerHeight()
* @result 1000
*
* @name outerHeight
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.outerHeight = function() {
       return this.get(0) == window || this.get(0) == document ?
               this.height() :
               this.get(0).offsetHeight;      
};

/**
* Returns the outer width value (including border) for the first matched element.
* Cannot be used on document or window.
*
* @example $("#testdiv").outerWidth()
* @result 1000
*
* @name outerWidth
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.outerWidth = function() {
       return this.get(0) == window || this.get(0) == document ?
               this.width() :
               this.get(0).offsetWidth;        
};

/**
* Returns how many pixels the user has scrolled to the right (scrollLeft).
* Works on containers with overflow: auto and window/document.
*
* @example $("#testdiv").scrollLeft()
* @result 100
*
* @name scrollLeft
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.scrollLeft = function() {
       if ( this.get(0) == window || this.get(0) == document )
               return self.pageXOffset ||
                       jQuery.boxModel && document.documentElement.scrollLeft ||
                       document.body.scrollLeft;
       
       return this.get(0).scrollLeft;
};

/**
* Returns how many pixels the user has scrolled to the bottom (scrollTop).
* Works on containers with overflow: auto and window/document.
*
* @example $("#testdiv").scrollTop()
* @result 100
*
* @name scrollTop
* @type Number
* @cat Plugins/Dimensions
*/
jQuery.fn.scrollTop = function() {
       if ( this.get(0) == window || this.get(0) == document )
               return self.pageYOffset ||
                       jQuery.boxModel && document.documentElement.scrollTop ||
                       document.body.scrollTop;

       return this.get(0).scrollTop;
};

/**
* This returns an object with top, left, width, height, borderLeft,
* borderTop, marginLeft, marginTop, scrollLeft, scrollTop,
* pageXOffset, pageYOffset.
*
* The top and left values include the scroll offsets but the
* scrollLeft and scrollTop properties of the returned object
* are the combined scroll offets of the parent elements
* (not including the window scroll offsets). This is not the
* same as the element's scrollTop and scrollLeft.
*
* For accurate readings make sure to use pixel values.
*
* @name offset
* @type Object
* @cat Plugins/Dimensions
* @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
*/
/**
* This returns an object with top, left, width, height, borderLeft,
* borderTop, marginLeft, marginTop, scrollLeft, scrollTop,
* pageXOffset, pageYOffset.
*
* The top and left values include the scroll offsets but the
* scrollLeft and scrollTop properties of the returned object
* are the combined scroll offets of the parent elements
* (not including the window scroll offsets). This is not the
* same as the element's scrollTop and scrollLeft.
*
* For accurate readings make sure to use pixel values.
*
* @name offset
* @type Object
* @param String refElement This is an expression. The offset returned will be relative to the first matched element.
* @cat Plugins/Dimensions
* @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
*/
/**
* This returns an object with top, left, width, height, borderLeft,
* borderTop, marginLeft, marginTop, scrollLeft, scrollTop,
* pageXOffset, pageYOffset.
*
* The top and left values include the scroll offsets but the
* scrollLeft and scrollTop properties of the returned object
* are the combined scroll offets of the parent elements
* (not including the window scroll offsets). This is not the
* same as the element's scrollTop and scrollLeft.
*
* For accurate readings make sure to use pixel values.
*
* @name offset
* @type Object
* @param jQuery refElement The offset returned will be relative to the first matched element.
* @cat Plugins/Dimensions
* @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
*/
/**
* This returns an object with top, left, width, height, borderLeft,
* borderTop, marginLeft, marginTop, scrollLeft, scrollTop,
* pageXOffset, pageYOffset.
*
* The top and left values include the scroll offsets but the
* scrollLeft and scrollTop properties of the returned object
* are the combined scroll offets of the parent elements
* (not including the window scroll offsets). This is not the
* same as the element's scrollTop and scrollLeft.
*
* For accurate readings make sure to use pixel values.
*
* @name offset
* @type Object
* @param HTMLElement refElement The offset returned will be relative to this element.
* @cat Plugins/Dimensions
* @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
*/
jQuery.fn.offset = function(refElem) {
       if (!this[0]) throw 'jQuery.fn.offset requires an element.';


       refElem = (refElem) ? jQuery(refElem)[0] : null;
       var x = 0, y = 0, elem = this[0], parent = this[0], sl = 0, st = 0;
       do {
               if (parent.tagName == 'BODY' || parent.tagName == 'HTML') {
                       // Safari and IE don't add margin for static and relative
                       if ((jQuery.browser.safari || jQuery.browser.msie) && jQuery.css(parent, 'position') != 'absolute') {
                               x += parseInt(jQuery.css(parent, 'marginLeft')) || 0;
                               y += parseInt(jQuery.css(parent, 'marginTop'))  || 0;
                       }
                       break;
               }

               x += parent.offsetLeft || 0;
               y += parent.offsetTop  || 0;

               // Mozilla and IE do not add the border
               if (jQuery.browser.mozilla || jQuery.browser.msie) {
                       // missed correction
                       //x += parseInt(jQuery.css(parent, 'borderLeftWidth')) || 0;
                       y += parseInt(jQuery.css(parent, 'borderTopWidth'))  || 0;
               }

               // Mozilla removes the border if the parent has overflow hidden
               if (jQuery.browser.mozilla && jQuery.css(parent, 'overflow') == 'hidden') {
                       x += parseInt(jQuery.css(parent, 'borderLeftWidth')) || 0;
                       y += parseInt(jQuery.css(parent, 'borderTopWidth'))  || 0;
               }

               // Need to get scroll offsets in-between offsetParents
               var op = parent.offsetParent;
               do {
                       sl += parent.scrollLeft || 0;
                       st += parent.scrollTop  || 0;
                       parent = parent.parentNode;
               } while (parent != op);
       } while (parent);

       if (refElem) { // Get the relative offset
               var offset = jQuery(refElem).offset();
               x  = x  - offset.left;
               y  = y  - offset.top;
               sl = sl - offset.scrollLeft;
               st = st - offset.scrollTop;
       }

       // Safari and Opera do not add the border for the element
       if (jQuery.browser.safari || jQuery.browser.opera) {
               x += parseInt(jQuery.css(elem, 'borderLeftWidth')) || 0;
               y += parseInt(jQuery.css(elem, 'borderTopWidth'))  || 0;
       }

       return {
               top:  y - st,
               left: x - sl,
               width:  elem.offsetWidth,
               height: elem.offsetHeight,
               borderTop:  parseInt(jQuery.css(elem, 'borderTopWidth'))  || 0,
               borderLeft: parseInt(jQuery.css(elem, 'borderLeftWidth')) || 0,
               marginTop:  parseInt(jQuery.css(elem, 'marginTopWidth'))  || 0,
               marginLeft: parseInt(jQuery.css(elem, 'marginLeftWidth')) || 0,
               scrollTop:  st,
               scrollLeft: sl,
               pageYOffset: window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop  || 0,
               pageXOffset: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0
       };
};