1 // OMG its another non-W3C standard browser 
  2 var REGEX_HTML_BODY = /^body|html$/i,
  3 /** @private */
  4 getWIN = function( elem ) {
  5 	return ("scrollTo" in elem && elem.document) ?
  6 		elem :
  7 		elem.nodeType === 9 ?
  8 			elem.defaultView || elem.parentWindow :
  9 			false;
 10 };
 11 
 12 if( "getBoundingClientRect" in DOC.documentElement ){
 13 	/**
 14 	 * @description to get the top, left offset of an element
 15 	 * @param {Element} elem the element to get the offset of
 16 	 * @returns {Object} top, left
 17 	 */
 18 	Simples.offset = function( elem ){
 19 
 20 		if ( !elem || !elem.ownerDocument ) {
 21 			return null;
 22 		}
 23 
 24 		if ( elem === elem.ownerDocument.body ) {
 25 			return Simples.bodyOffset( elem );
 26 		}
 27 
 28 		var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body,
 29 			docElem = doc.documentElement, win = getWIN(doc),
 30 			clientTop  = docElem.clientTop  || body.clientTop  || 0,
 31 			clientLeft = docElem.clientLeft || body.clientLeft || 0,
 32 			scrollTop  = (win.pageYOffset || Simples.support.boxModel && docElem.scrollTop  || body.scrollTop ),
 33 			scrollLeft = (win.pageXOffset || Simples.support.boxModel && docElem.scrollLeft || body.scrollLeft),
 34 			top  = box.top  + scrollTop  - clientTop, left = box.left + scrollLeft - clientLeft;
 35 
 36 		return { top: top, left: left };
 37 	};	                 
 38 } else {
 39 	Simples.offset = function( elem ) {
 40 
 41 		if ( !elem || !elem.ownerDocument ) {
 42 			return null;
 43 		}
 44 
 45 		if ( elem === elem.ownerDocument.body ) {
 46 			return Simples.bodyOffset( elem );
 47 		}
 48 
 49 		Simples.offset.init();
 50 
 51 		var offsetParent = elem.offsetParent, prevOffsetParent = elem,
 52 			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
 53 			body = doc.body, defaultView = doc.defaultView,
 54 			prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
 55 			top = elem.offsetTop, left = elem.offsetLeft;
 56 
 57 		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
 58 			if ( Simples.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
 59 				break;
 60 			}
 61 
 62 			computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
 63 			top  -= elem.scrollTop;
 64 			left -= elem.scrollLeft;
 65 
 66 			if ( elem === offsetParent ) {
 67 				top  += elem.offsetTop;
 68 				left += elem.offsetLeft;
 69 
 70 				if ( Simples.offset.doesNotAddBorder && !(Simples.offset.doesAddBorderForTableAndCells && (/^t(able|d|h)$/i).test(elem.nodeName)) ) {
 71 					top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
 72 					left += parseFloat( computedStyle.borderLeftWidth ) || 0;
 73 				}
 74 
 75 				prevOffsetParent = offsetParent;
 76 				offsetParent = elem.offsetParent;
 77 			}
 78 
 79 			if ( Simples.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
 80 				top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
 81 				left += parseFloat( computedStyle.borderLeftWidth ) || 0;
 82 			}
 83 
 84 			prevComputedStyle = computedStyle;
 85 		}
 86 
 87 		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
 88 			top  += body.offsetTop;
 89 			left += body.offsetLeft;
 90 		}
 91 
 92 		if ( Simples.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
 93 			top  += Math.max( docElem.scrollTop, body.scrollTop );
 94 			left += Math.max( docElem.scrollLeft, body.scrollLeft );
 95 		}
 96 
 97 		return { top: top, left: left };
 98 	};
 99 }
100 /** @private */
101 Simples.offset.init = function(){
102 	var body = DOC.body, container = DOC.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( Simples.currentCSS(body, "marginTop", true) ) || 0,
103 		html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
104 
105 	Simples.merge( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
106 
107 	container.innerHTML = html;
108 	body.insertBefore( container, body.firstChild );
109 	innerDiv = container.firstChild;
110 	checkDiv = innerDiv.firstChild;
111 	td = innerDiv.nextSibling.firstChild.firstChild;
112 
113 	Simples.offset.doesNotAddBorder = (checkDiv.offsetTop !== 5);
114 	Simples.offset.doesAddBorderForTableAndCells = (td.offsetTop === 5);
115 
116 	checkDiv.style.position = "fixed";
117 	checkDiv.style.top = "20px";
118 
119 	// safari subtracts parent border width here which is 5px
120 	Simples.offset.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
121 	checkDiv.style.position = checkDiv.style.top = "";
122 
123 	innerDiv.style.overflow = "hidden";
124 	innerDiv.style.position = "relative";
125 
126 	Simples.offset.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
127 
128 	Simples.offset.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
129 
130 	body.removeChild( container );
131 	body = container = innerDiv = checkDiv = table = td = null;
132 	Simples.offset.init = Simples.noop;
133 };
134 
135 Simples.merge( /** @lends Simples */ {
136 	/**
137 	 * @description to get the offset of the body 
138 	 * @param {Body} body body element to measure
139 	 * @returns {Object} top, left
140 	 */
141 	bodyOffset : function( body ) {
142 		var top = body.offsetTop, left = body.offsetLeft;
143 
144 		Simples.offset.init();
145 
146 		if ( Simples.offset.doesNotIncludeMarginInBodyOffset ) {
147 			top  += parseFloat( Simples.currentCSS(body, "marginTop",  true) ) || 0;
148 			left += parseFloat( Simples.currentCSS(body, "marginLeft", true) ) || 0;
149 		}
150 
151 		return { top: top, left: left };
152 	},
153 	/**
154 	 * @description to set the offset of the top and left of an element passed on its current offset
155 	 * @param {Element} elem element to set the offset on
156 	 * @param {Object} options
157 	 * @param {Number} options.top the top offset desired
158 	 * @param {Number} options.left	the left offset desired
159 	 */
160 	setOffset : function( elem, options ) {
161 		var position = Simples.currentCSS( elem, "position" );
162 
163 		// set position first, in-case top/left are set even on static elem
164 		if ( position === "static" ) {
165 			elem.style.position = "relative";
166 		}
167 
168 		var curElem    = Simples( elem ),
169 			curOffset  = curElem.offset(),
170 			curCSSTop  = Simples.currentCSS( elem, TOP, true ),
171 			curCSSLeft = Simples.currentCSS( elem, LEFT, true ),
172 			calculatePosition = (position === "absolute" && (curCSSTop === 'auto' || curCSSLeft === 'auto' ) ),
173 			props = {}, curPosition = {}, curTop, curLeft;
174 
175 		// need to be able to calculate position if either top or left is auto and position is absolute
176 		if ( calculatePosition ) {
177 			curPosition = curElem.position();
178 		}
179 
180 		curTop  = calculatePosition ? curPosition.top  : parseInt( curCSSTop,  10 ) || 0;
181 		curLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;
182 
183 		if (options.top != null) {
184 			props.top = (options.top - curOffset.top) + curTop;
185 		}
186 		if (options.left != null) {
187 			props.left = (options.left - curOffset.left) + curLeft;
188 		}
189 
190 		curElem.css( props );
191 	},
192 	/**
193 	 * @description to get the offsetParent of an element
194 	 * @param {Element} elem the element to get the offsetParent of
195 	 * @returns {Element}
196 	 */
197 	offsetParent : function( elem ) {
198 		var offsetParent = elem.offsetParent || DOC.body;
199 		while ( offsetParent && (!REGEX_HTML_BODY.test(offsetParent.nodeName) && Simples.currentCSS(offsetParent, "position") === "static") ) {
200 			offsetParent = offsetParent.offsetParent;
201 		}
202 
203 		return offsetParent;
204 	},
205 	/**
206 	 * @description to get the position of the element
207 	 * @param {Element} elem to get the position of
208 	 * @returns {Object} top, left
209 	 */
210 	position: function( elem ) {
211 
212 		// Get *real* offsetParent
213 		var offsetParent = Simples.offsetParent( elem ),
214 
215 		// Get correct offsets
216 		offset       = Simples.offset( elem ),
217 		parentOffset = REGEX_HTML_BODY.test(offsetParent.nodeName) ? { top: 0, left: 0 } : Simples.offset( offsetParent );
218 
219 		// Subtract element margins
220 		// note: when an element has margin: auto the offsetLeft and marginLeft
221 		// are the same in Safari causing offset.left to incorrectly be 0
222 		offset.top  -= parseFloat( Simples.currentCSS(elem, "marginTop",  true) ) || 0;
223 		offset.left -= parseFloat( Simples.currentCSS(elem, "marginLeft", true) ) || 0;
224 
225 		// Add offsetParent borders
226 		parentOffset.top  += parseFloat( Simples.currentCSS(offsetParent, "borderTopWidth",  true) ) || 0;
227 		parentOffset.left += parseFloat( Simples.currentCSS(offsetParent, "borderLeftWidth", true) ) || 0;
228 
229 		// Subtract the two offsets
230 		return {
231 			top:  offset.top  - parentOffset.top,
232 			left: offset.left - parentOffset.left
233 		};
234 	},
235 	/**
236 	 * @description To set the scrollTop / scrollLeft for a given element
237 	 * @param {Element} elem element to set the scroll on
238 	 * @param {String} name 'top' or 'left'
239 	 * @param {Number} value
240 	 */
241 	setScroll : function( elem, name, val ){
242 		win = getWIN( elem );
243 
244 		if ( win ) {
245 			win.scrollTo(
246 				name === LEFT ? val : Simples.getScroll( win, LEFT ),
247 				name === TOP ? val : Simples.getScroll( win, TOP )
248 			);
249 
250 		} else {
251 			name = name === TOP ? "scrollTop" : "scrollLeft";
252 			elem[ name ] = val;
253 		}
254 	},
255 	/**
256 	 * @description To retrieve the scrollTop / scrollLeft for a given element
257 	 * @param {Element} elem element to get the scroll of
258 	 * @param {String} name 'top' or 'left'
259 	 * @returns {Number} the value of the scrollTop / scrollLeft
260 	 */	
261 	getScroll : function( elem, name ){
262 		var isTop = name === TOP;
263 		name = isTop ? "scrollTop" : "scrollLeft";
264 		win = getWIN( elem );
265 
266 		// Return the scroll offset
267 		return win ? ( ("pageXOffset" in win) ? win[ isTop ? "pageYOffset" : "pageXOffset" ] : Simples.support.boxModel && win.document.documentElement[ name ] || win.document.body[ name ] ) : elem[ name ];		
268 	}
269 });
270 
271 Simples.extend( /** @lends Simples.fn */ {
272 	/**
273 	 * @description To set or retrieve the offset of the selected elements on the Simples object
274 	 * @param {Object} options object with top and / or left specified to set the offset
275 	 * @returns {Number|Simples} the value of the offset or Simples object
276 	 */	
277 	offset : function( options ){
278 
279 		if ( options ) {
280 			var len = this.length;
281 			while( len ){
282 				Simples.setOffset( this[ --len ], options );
283 			}
284 			return this;
285 		}
286 
287 		return this[0] ? Simples.offset( this[0] ) : null;
288 	},
289 	/**
290 	 * @description To return the same object with the offsetParents added in place of the selected elements
291 	 */	
292 	offsetParent : function(){
293 		var len = this.length;
294 		while( len ){
295 			this[ --len ] = Simples.offsetParent( this[ len ] );
296 		}
297 		return this;
298 	},
299 	/**
300 	 * @description To retrieve or set the scrollTop / scrollLeft elements on the simples object, if no value is provided the first element has the value return
301 	 * @param {String} name 'top' or 'left'
302 	 * @param {Number} val the value to set the offset to
303 	 * @returns {Number|Simples} the value of the scrollTop / scrollLeft or Simples object
304 	 */	
305 	scroll : function( name, val ){
306 		if( val !== UNDEF ){
307 			var len = this.length;
308 			while( len ){
309 				Simples.setScroll( this[ --len ], name, val );
310 			}
311 			return this;
312 		}
313 		return this[0] ? Simples.getScroll( this[0], name ) : null;
314 	},
315 	/**
316 	 * @description To retrieve or set the scrollTop / scrollLeft elements on the simples object
317 	 * @param {String} name 'top' or 'left'
318 	 * @param {Number} val the value to set the offset to
319 	 * @returns {Number} the value of the scrollTop / scrollLeft
320 	 */	
321 	position : function(){
322 		return this[0] ? Simples.position( this[0] ) : null;
323 	}
324 });