dojo.provide("dijit._base.scroll"); dijit.scrollIntoView = function(/* DomNode */node){ // summary // Scroll the passed node into view, if it is not. // don't rely on that node.scrollIntoView works just because the function is there // it doesnt work in Konqueror or Opera even though the function is there and probably // not safari either // native scrollIntoView() causes FF3's whole window to scroll if there is no scroll bar // on the immediate parent // dont like browser sniffs implementations but sometimes you have to use it // It's not enough just to scroll the menu node into view if // node.scrollIntoView hides part of the parent's scrollbar, // so just manage the parent scrollbar ourselves node = dojo.byId(node); var body = node.ownerDocument.body; var html = body.parentNode; if(dojo.isFF == 2 || node == body || node == html){ // FF2 is perfect, too bad FF3 is not node.scrollIntoView(false); // short-circuit to native if possible return; } var rtl = !dojo._isBodyLtr(); var strict = dojo.doc.compatMode != 'BackCompat'; // not the same as !dojo.isQuirks var scrollRoot = (strict && !dojo.isSafari)? html : body; function addPseudoAttrs(element){ var parent = element.parentNode; var offsetParent = element.offsetParent; if(offsetParent == null){ // process only 1 of BODY/HTML element = scrollRoot; offsetParent = html; parent = null; } // all the V/H object members below are to reuse code for both directions element._offsetParent = (offsetParent == body)? scrollRoot : offsetParent; element._parent = (parent == body)? scrollRoot : parent; element._start = { H:element.offsetLeft, V:element.offsetTop }; element._scroll = { H:element.scrollLeft, V:element.scrollTop }; element._renderedSize = { H: element.offsetWidth, V: element.offsetHeight }; var bp = dojo._getBorderExtents(element); element._borderStart = { H:bp.l, V:bp.t }; element._borderSize = { H:bp.w, V:bp.h }; element._clientSize = (element._offsetParent == html && dojo.isSafari && strict)? { H:html.clientWidth, V:html.clientHeight } : { H:element.clientWidth, V:element.clientHeight }; element._scrollBarSize = { V: null, H: null }; for(var dir in element._scrollBarSize){ // for both x and y directions var scrollBar = element._renderedSize[dir] - element._clientSize[dir] - element._borderSize[dir]; element._scrollBarSize[dir] = (element._clientSize[dir] > 0 && scrollBar >= 15 && scrollBar <= 17)? scrollBar : 0; // sanity check } element._isScrollable = { V: null, H: null }; for(dir in element._isScrollable){ // for both x and y directions var otherDir = dir=="H"? "V" : "H"; element._isScrollable[dir] = element == scrollRoot || element._scroll[dir] || element._scrollBarSize[otherDir]; } } var parent = node; while(parent != null){ addPseudoAttrs(parent); var next = parent._parent; if(next){ next._child = parent; } parent = next; } for(var dir in scrollRoot._renderedSize){ scrollRoot._renderedSize[dir] = Math.min(scrollRoot._clientSize[dir], scrollRoot._renderedSize[dir]); } var element = node; while(element != scrollRoot){ parent = element._parent; if(parent.tagName == "TD"){ var table = parent._parent._parent._parent; // point to TABLE if(table._offsetParent == element._offsetParent && parent._offsetParent != element._offsetParent){ parent = table; // child of TD has the same offsetParent as TABLE, so skip TD, TR, and TBODY (ie. verticalslider) } } // check if this node and its parent share the same offsetParent var startIsRelative = element == scrollRoot || (parent._offsetParent != element._offsetParent); for(dir in element._start){ // for both x and y directions var otherDir = dir=="H"? "V" : "H"; if(rtl && dir=="H" && (dojo.isSafari || dojo.isIE) && parent._clientSize.H > 0){ // scroll starts on the right var delta = parent.scrollWidth - parent._clientSize.H; if(delta > 0){ parent._scroll.H -= delta; } // match FF3 which has cool negative scrollLeft values } if(dojo.isIE && parent._offsetParent.tagName == "TABLE"){ // make it consistent with Safari and FF3 and exclude the starting TABLE border of TABLE children parent._start[dir] -= parent._offsetParent._borderStart[dir]; parent._borderStart[dir] = parent._borderSize[dir] = 0; } if(parent._clientSize[dir] == 0){ // TABLE on Safari3/FF3, and TBODY on IE6/7 parent._renderedSize[dir] = parent._clientSize[dir] = parent._child._clientSize[dir]; if(rtl && dir=="H"){ parent._start[dir] -= parent._renderedSize[dir]; } }else{ parent._renderedSize[dir] -= parent._borderSize[dir] + parent._scrollBarSize[dir]; } parent._start[dir] += parent._borderStart[dir]; // underflow = visible gap between parent and this node taking scrolling into account // if negative, part of the node is obscured by the parent's beginning and should be scrolled to become visible var underflow = element._start[dir] - (startIsRelative? 0 : parent._start[dir]) - parent._scroll[dir]; // if positive, number of pixels obscured by the parent's end var overflow = underflow + element._renderedSize[dir] - parent._renderedSize[dir]; var scrollAmount, scrollAttr = (dir=="H")? "scrollLeft" : "scrollTop"; // see if we should scroll forward or backward var reverse = (dir=="H" && rtl); // flip everything var underflowScroll = reverse? -overflow : underflow; var overflowScroll = reverse? -underflow : overflow; if(underflowScroll <= 0){ scrollAmount = underflowScroll; }else if(overflowScroll <= 0){ scrollAmount = 0; }else if(underflowScroll < overflowScroll){ scrollAmount = underflowScroll; }else{ scrollAmount = overflowScroll; } var scrolledAmount = 0; if(scrollAmount != 0){ var oldScroll = parent[scrollAttr]; parent[scrollAttr] += reverse? -scrollAmount : scrollAmount; // actually perform the scroll scrolledAmount = parent[scrollAttr] - oldScroll; // in case the scroll failed underflow -= scrolledAmount; overflowScroll -= reverse? -scrolledAmount : scrolledAmount; } parent._renderedSize[dir] = element._renderedSize[dir] + parent._scrollBarSize[dir] - // check for isScrollable since a nonscrolling parent could be smaller than the child but the child is fully visible ((parent._isScrollable[dir] && overflowScroll > 0)? overflowScroll : 0); // only show portion of the parent parent._start[dir] += (underflow >= 0 || !parent._isScrollable[dir])? underflow : 0; } element = parent; // now see if the parent needs to be scrolled as well } };