2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('dom-screen', function(Y) {
13 * Adds position and region management functionality to DOM.
15 * @submodule dom-screen
19 var DOCUMENT_ELEMENT = 'documentElement',
20 COMPAT_MODE = 'compatMode',
21 POSITION = 'position',
23 RELATIVE = 'relative',
26 _BACK_COMPAT = 'BackCompat',
28 BORDER_LEFT_WIDTH = 'borderLeftWidth',
29 BORDER_TOP_WIDTH = 'borderTopWidth',
30 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
31 GET_COMPUTED_STYLE = 'getComputedStyle',
35 // TODO: how about thead/tbody/tfoot/tr?
36 // TODO: does caption matter?
37 RE_TABLE = /^t(?:able|d|h)$/i,
42 if (Y.config.doc[COMPAT_MODE] !== 'quirks') {
43 SCROLL_NODE = DOCUMENT_ELEMENT;
51 * Returns the inner height of the viewport (exludes scrollbar).
53 * @return {Number} The current height of the viewport.
55 winHeight: function(node) {
56 var h = Y_DOM._getWinSize(node).height;
61 * Returns the inner width of the viewport (exludes scrollbar).
63 * @return {Number} The current width of the viewport.
65 winWidth: function(node) {
66 var w = Y_DOM._getWinSize(node).width;
73 * @return {Number} The current height of the document.
75 docHeight: function(node) {
76 var h = Y_DOM._getDocSize(node).height;
77 return Math.max(h, Y_DOM._getWinSize(node).height);
83 * @return {Number} The current width of the document.
85 docWidth: function(node) {
86 var w = Y_DOM._getDocSize(node).width;
87 return Math.max(w, Y_DOM._getWinSize(node).width);
91 * Amount page has been scroll horizontally
93 * @return {Number} The current amount the screen is scrolled horizontally.
95 docScrollX: function(node, doc) {
96 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
97 var dv = doc.defaultView,
98 pageOffset = (dv) ? dv.pageXOffset : 0;
99 return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset);
103 * Amount page has been scroll vertically
105 * @return {Number} The current amount the screen is scrolled vertically.
107 docScrollY: function(node, doc) {
108 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
109 var dv = doc.defaultView,
110 pageOffset = (dv) ? dv.pageYOffset : 0;
111 return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset);
115 * Gets the current position of an element based on page coordinates.
116 * Element must be part of the DOM tree to have page coordinates
117 * (display:none or elements not appended return false).
119 * @param element The target element
120 * @return {Array} The XY position of the element
122 TODO: test inDocument/display?
125 if (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
126 return function(node) {
138 if (node && node.tagName) {
139 doc = node.ownerDocument;
140 rootNode = doc[DOCUMENT_ELEMENT];
142 // inline inDoc check for perf
143 if (rootNode.contains) {
144 inDoc = rootNode.contains(node);
146 inDoc = Y.DOM.contains(rootNode, node);
150 scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc);
151 scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc);
152 box = node[GET_BOUNDING_CLIENT_RECT]();
153 xy = [box.left, box.top];
158 mode = doc[COMPAT_MODE];
159 bLeft = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
160 bTop = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
163 if (mode !== _BACK_COMPAT) {
169 if ((mode == _BACK_COMPAT)) {
170 if (bLeft !== MEDIUM) {
171 off1 = parseInt(bLeft, 10);
173 if (bTop !== MEDIUM) {
174 off2 = parseInt(bTop, 10);
183 if ((scrollTop || scrollLeft)) {
184 if (!Y.UA.ios || (Y.UA.ios >= 4.2)) {
191 xy = Y_DOM._getOffset(node);
197 return function(node) { // manually calculate by crawling up offsetParents
198 //Calculate the Top and Left border sizes (assumes pixels)
207 if (Y_DOM.inDoc(node)) {
208 xy = [node.offsetLeft, node.offsetTop];
209 doc = node.ownerDocument;
211 // TODO: refactor with !! or just falsey
212 bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
214 // TODO: worth refactoring for TOP/LEFT only?
215 while ((parentNode = parentNode.offsetParent)) {
216 xy[0] += parentNode.offsetLeft;
217 xy[1] += parentNode.offsetTop;
219 xy = Y_DOM._calcBorders(parentNode, xy);
223 // account for any scrolled ancestors
224 if (Y_DOM.getStyle(node, POSITION) != FIXED) {
227 while ((parentNode = parentNode.parentNode)) {
228 scrollTop = parentNode.scrollTop;
229 scrollLeft = parentNode.scrollLeft;
231 //Firefox does something funky with borders when overflow is not visible.
232 if (Y.UA.gecko && (Y_DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
233 xy = Y_DOM._calcBorders(parentNode, xy);
237 if (scrollTop || scrollLeft) {
242 xy[0] += Y_DOM.docScrollX(node, doc);
243 xy[1] += Y_DOM.docScrollY(node, doc);
246 //Fix FIXED position -- add scrollbars
247 xy[0] += Y_DOM.docScrollX(node, doc);
248 xy[1] += Y_DOM.docScrollY(node, doc);
251 xy = Y_DOM._getOffset(node);
258 }(),// NOTE: Executing for loadtime branching
261 * Gets the current X position of an element based on page coordinates.
262 * Element must be part of the DOM tree to have page coordinates
263 * (display:none or elements not appended return false).
265 * @param element The target element
266 * @return {Int} The X position of the element
269 getX: function(node) {
270 return Y_DOM.getXY(node)[0];
274 * Gets the current Y position of an element based on page coordinates.
275 * Element must be part of the DOM tree to have page coordinates
276 * (display:none or elements not appended return false).
278 * @param element The target element
279 * @return {Int} The Y position of the element
282 getY: function(node) {
283 return Y_DOM.getXY(node)[1];
287 * Set the position of an html element in page coordinates.
288 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
290 * @param element The target element
291 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
292 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
294 setXY: function(node, xy, noRetry) {
295 var setStyle = Y_DOM.setStyle,
302 pos = Y_DOM.getStyle(node, POSITION);
304 delta = Y_DOM._getOffset(node);
305 if (pos == 'static') { // default to relative
307 setStyle(node, POSITION, pos);
309 currentXY = Y_DOM.getXY(node);
311 if (xy[0] !== null) {
312 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
315 if (xy[1] !== null) {
316 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
320 newXY = Y_DOM.getXY(node);
321 if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
322 Y_DOM.setXY(node, xy, true);
331 * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
332 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
334 * @param element The target element
335 * @param {Int} x The X values for new position (coordinates are page-based)
337 setX: function(node, x) {
338 return Y_DOM.setXY(node, [x, null]);
342 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
343 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
345 * @param element The target element
346 * @param {Int} y The Y values for new position (coordinates are page-based)
348 setY: function(node, y) {
349 return Y_DOM.setXY(node, [null, y]);
354 * @description Swap the xy position with another node
355 * @param {Node} node The node to swap with
356 * @param {Node} otherNode The other node to swap with
359 swapXY: function(node, otherNode) {
360 var xy = Y_DOM.getXY(node);
361 Y_DOM.setXY(node, Y_DOM.getXY(otherNode));
362 Y_DOM.setXY(otherNode, xy);
365 _calcBorders: function(node, xy2) {
366 var t = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
367 l = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
369 if (RE_TABLE.test(node.tagName)) {
379 _getWinSize: function(node, doc) {
380 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc;
381 var win = doc.defaultView || doc.parentWindow,
382 mode = doc[COMPAT_MODE],
385 root = doc[DOCUMENT_ELEMENT];
387 if ( mode && !Y.UA.opera ) { // IE, Gecko
388 if (mode != 'CSS1Compat') { // Quirks
391 h = root.clientHeight;
392 w = root.clientWidth;
394 return { height: h, width: w };
397 _getDocSize: function(node) {
398 var doc = (node) ? Y_DOM._getDoc(node) : Y.config.doc,
399 root = doc[DOCUMENT_ELEMENT];
401 if (doc[COMPAT_MODE] != 'CSS1Compat') {
405 return { height: root.scrollHeight, width: root.scrollWidth };
416 getOffsets = function(r1, r2) {
417 var t = Math.max(r1[TOP], r2[TOP]),
418 r = Math.min(r1[RIGHT], r2[RIGHT]),
419 b = Math.min(r1[BOTTOM], r2[BOTTOM]),
420 l = Math.max(r1[LEFT], r2[LEFT]),
434 * Returns an Object literal containing the following about this element: (top, right, bottom, left)
437 * @param {HTMLElement} element The DOM element.
438 * @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
440 region: function(node) {
441 var xy = DOM.getXY(node),
445 ret = DOM._getRegion(
447 xy[0] + node.offsetWidth, // right
448 xy[1] + node.offsetHeight, // bottom
457 * Find the intersect information for the passes nodes.
460 * @param {HTMLElement} element The first element
461 * @param {HTMLElement | Object} element2 The element or region to check the interect with
462 * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
463 * @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
465 intersect: function(node, node2, altRegion) {
466 var r = altRegion || DOM.region(node), region = {},
471 region = DOM.region(n);
472 } else if (Y.Lang.isObject(node2)) {
478 off = getOffsets(region, r);
484 area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
485 yoff: ((off[BOTTOM] - off[TOP])),
486 xoff: (off[RIGHT] - off[LEFT]),
487 inRegion: DOM.inRegion(node, node2, false, altRegion)
492 * Check if any part of this node is in the passed region
495 * @param {Object} node2 The node to get the region from or an Object literal of the region
496 * $param {Boolean} all Should all of the node be inside the region
497 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
498 * @return {Boolean} True if in region, false if not.
500 inRegion: function(node, node2, all, altRegion) {
502 r = altRegion || DOM.region(node),
507 region = DOM.region(n);
508 } else if (Y.Lang.isObject(node2)) {
516 r[LEFT] >= region[LEFT] &&
517 r[RIGHT] <= region[RIGHT] &&
518 r[TOP] >= region[TOP] &&
519 r[BOTTOM] <= region[BOTTOM] );
521 off = getOffsets(region, r);
522 if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
532 * Check if any part of this element is in the viewport
533 * @method inViewportRegion
535 * @param {HTMLElement} element The DOM element.
536 * @param {Boolean} all Should all of the node be inside the region
537 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
538 * @return {Boolean} True if in region, false if not.
540 inViewportRegion: function(node, all, altRegion) {
541 return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
545 _getRegion: function(t, r, b, l) {
548 region[TOP] = region[1] = t;
549 region[LEFT] = region[0] = l;
552 region.width = region[RIGHT] - region[LEFT];
553 region.height = region[BOTTOM] - region[TOP];
559 * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
560 * @method viewportRegion
562 * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
564 viewportRegion: function(node) {
565 node = node || Y.config.doc.documentElement;
571 scrollX = DOM.docScrollX(node);
572 scrollY = DOM.docScrollY(node);
574 ret = DOM._getRegion(scrollY, // top
575 DOM.winWidth(node) + scrollX, // right
576 scrollY + DOM.winHeight(node), // bottom
586 }, '3.3.0' ,{requires:['dom-base', 'dom-style', 'event-base']});