cacert-testmgr/external/ZendFramework-1.9.5/externals/dojo/dijit/_base/scroll.js

224 lines
13 KiB
JavaScript
Raw Permalink Normal View History

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
//var testdir="H"; //debug
try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
node = dojo.byId(node);
var doc = dojo.doc;
var body = dojo.body();
var html = body.parentNode;
// if FF2 (which is perfect) or an untested browser, then use the native method
if((!(dojo.isFF >= 3 || dojo.isIE || dojo.isWebKit) || node == body || node == html) && (typeof node.scrollIntoView == "function")){ // FF2 is perfect, too bad FF3 is not
node.scrollIntoView(false); // short-circuit to native if possible
return;
}
var ltr = dojo._isBodyLtr();
var isIE8strict = dojo.isIE >= 8 && !compatMode;
var rtl = !ltr && !isIE8strict; // IE8 flips scrolling so pretend it's ltr
// body and html elements are all messed up due to browser bugs and inconsistencies related to doctype
// normalize the values before proceeding (FF2 is not listed since its native behavior is perfect)
// for computation simplification, client and offset width and height are the same for body and html
// strict: html: | body: | compatMode:
// width height | width height |------------
// ie*: clientW clientH | scrollW clientH | CSS1Compat
// ff3: clientW clientH |HscrollW clientH | CSS1Compat
// sf3: clientW clientH | clientW HclientH | CSS1Compat
// op9: clientW clientH |HscrollW clientH | CSS1Compat
// ---------------------------------------------|-----------
// none: html: | body: |
// width height | width height |
// ie*: BclientW BclientH | clientW clientH | BackCompat
// ff3: BclientW BclientH | clientW clientH | BackCompat
// sf3: clientW clientH | clientW HclientH | CSS1Compat
// op9: BclientW BclientH | clientW clientH | BackCompat
// ---------------------------------------------|-----------
// loose: html: | body: |
// width height | width height |
// ie*: clientW clientH | scrollW clientH | CSS1Compat
// ff3: BclientW BclientH | clientW clientH | BackCompat
// sf3: clientW clientH | clientW HclientH | CSS1Compat
// op9: clientW clientH |HscrollW clientH | CSS1Compat
var scrollRoot = body;
var compatMode = doc.compatMode == 'BackCompat';
if(compatMode){ // BODY is scrollable, HTML has same client size
// body client values already OK
html._offsetWidth = html._clientWidth = body._offsetWidth = body.clientWidth;
html._offsetHeight = html._clientHeight = body._offsetHeight = body.clientHeight;
}else{
if(dojo.isWebKit){
body._offsetWidth = body._clientWidth = html.clientWidth;
body._offsetHeight = body._clientHeight = html.clientHeight;
}else{
scrollRoot = html;
}
html._offsetHeight = html.clientHeight;
html._offsetWidth = html.clientWidth;
}
function isFixedPosition(element){
var ie = dojo.isIE;
return ((ie <= 6 || (ie >= 7 && compatMode))? false : (dojo.style(element, 'position').toLowerCase() == "fixed"));
}
function addPseudoAttrs(element){
var parent = element.parentNode;
var offsetParent = element.offsetParent;
if(offsetParent == null || isFixedPosition(element)){ // position:fixed has no real offsetParent
offsetParent = html; // prevents exeptions
parent = (element == body)? html : null;
}
// all the V/H object members below are to reuse code for both directions
element._offsetParent = offsetParent;
element._parent = parent;
//console.debug('parent = ' + (element._parentTag = element._parent?element._parent.tagName:'NULL'));
//console.debug('offsetParent = ' + (element._offsetParentTag = element._offsetParent.tagName));
var bp = dojo._getBorderExtents(element);
element._borderStart = { H:(isIE8strict && !ltr)? (bp.w-bp.l):bp.l, V:bp.t };
element._borderSize = { H:bp.w, V:bp.h };
element._scrolledAmount = { H:element.scrollLeft, V:element.scrollTop };
element._offsetSize = { H: element._offsetWidth||element.offsetWidth, V: element._offsetHeight||element.offsetHeight };
//console.debug('element = ' + element.tagName + ', '+testdir+' size = ' + element[testdir=='H'?"offsetWidth":"offsetHeight"] + ', parent = ' + element._parentTag);
// IE8 flips everything in rtl mode except offsetLeft and borderLeft - so manually change offsetLeft to offsetRight here
element._offsetStart = { H:(isIE8strict && !ltr)? offsetParent.clientWidth-element.offsetLeft-element._offsetSize.H:element.offsetLeft, V:element.offsetTop };
//console.debug('element = ' + element.tagName + ', initial _relativeOffset = ' + element._offsetStart[testdir]);
element._clientSize = { H:element._clientWidth||element.clientWidth, V:element._clientHeight||element.clientHeight };
if(element != body && element != html && element != node){
for(var dir in element._offsetSize){ // for both x and y directions
var scrollBarSize = element._offsetSize[dir] - element._clientSize[dir] - element._borderSize[dir];
//if(dir==testdir)console.log('element = ' + element.tagName + ', scrollBarSize = ' + scrollBarSize + ', clientSize = ' + element._clientSize[dir] + ', offsetSize = ' + element._offsetSize[dir] + ', border size = ' + element._borderSize[dir]);
var hasScrollBar = element._clientSize[dir] > 0 && scrollBarSize > 0; // can't check for a specific scrollbar size since it changes dramatically as you zoom
//if(dir==testdir)console.log('element = ' + element.tagName + ', hasScrollBar = ' + hasScrollBar);
if(hasScrollBar){
element._offsetSize[dir] -= scrollBarSize;
if(dojo.isIE && rtl && dir=="H"){ element._offsetStart[dir] += scrollBarSize; }
}
}
}
}
var element = node;
while(element != null){
if(isFixedPosition(element)){ node.scrollIntoView(false); return; } //TODO: handle without native call
addPseudoAttrs(element);
element = element._parent;
}
if(dojo.isIE && node._parent){ // if no parent, then offsetParent._borderStart may not tbe set
var offsetParent = node._offsetParent;
//console.debug('adding offsetParent borderStart = ' + offsetParent._borderStart.H + ' to node offsetStart');
node._offsetStart.H += offsetParent._borderStart.H;
node._offsetStart.V += offsetParent._borderStart.V;
}
if(dojo.isIE >= 7 && scrollRoot == html && rtl && body._offsetStart && body._offsetStart.H == 0){ // IE7 bug
var scroll = html.scrollWidth - html._offsetSize.H;
if(scroll > 0){
//console.debug('adjusting html scroll by ' + -scroll + ', scrollWidth = ' + html.scrollWidth + ', offsetSize = ' + html._offsetSize.H);
body._offsetStart.H = -scroll;
}
}
if(dojo.isIE <= 6 && !compatMode){
html._offsetSize.H += html._borderSize.H;
html._offsetSize.V += html._borderSize.V;
}
// eliminate offsetLeft/Top oddities by tweaking scroll for ease of computation
if(rtl && body._offsetStart && scrollRoot == html && html._scrolledAmount){
var ofs = body._offsetStart.H;
if(ofs < 0){
html._scrolledAmount.H += ofs;
body._offsetStart.H = 0;
}
}
element = node;
while(element){
var parent = element._parent;
if(!parent){ break; }
//console.debug('element = ' + element.tagName + ', parent = ' + parent.tagName + ', parent offsetSize = ' + parent._offsetSize[testdir] + ' clientSize = ' + parent._clientSize[testdir]);
if(parent.tagName == "TD"){
var table = parent._parent._parent._parent; // point to TABLE
if(parent != 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 relative = element._offsetParent == parent;
//console.debug('element = ' + element.tagName + ', offsetParent = ' + element._offsetParent.tagName + ', parent = ' + parent.tagName + ', relative = ' + relative);
for(var dir in element._offsetStart){ // for both x and y directions
var otherDir = dir=="H"? "V" : "H";
if(rtl && dir=="H" && (parent != html) && (parent != body) && (dojo.isIE || dojo.isWebKit) && parent._clientSize.H > 0 && parent.scrollWidth > parent._clientSize.H){ // scroll starts on the right
var delta = parent.scrollWidth - parent._clientSize.H;
//console.debug('rtl scroll delta = ' + delta + ', changing ' + parent.tagName + ' scroll from ' + parent._scrolledAmount.H + ' to ' + (parent._scrolledAmount.H - delta) + ', parent.scrollWidth = ' + parent.scrollWidth + ', parent._clientSize.H = ' + parent._clientSize.H);
if(delta > 0){
parent._scrolledAmount.H -= delta;
} // match FF3 which has cool negative scrollLeft values
}
if(parent._offsetParent.tagName == "TABLE"){ // make it consistent
if(dojo.isIE){ // make it consistent with Safari and FF3 and exclude the starting TABLE border of TABLE children
parent._offsetStart[dir] -= parent._offsetParent._borderStart[dir];
parent._borderStart[dir] = parent._borderSize[dir] = 0;
}
else{
parent._offsetStart[dir] += parent._offsetParent._borderStart[dir];
}
}
//if(dir==testdir)console.debug('border start = ' + parent._borderStart[dir] + ', border size = ' + parent._borderSize[dir]);
if(dojo.isIE){
//if(dir==testdir)console.debug('changing parent offsetStart from ' + parent._offsetStart[dir] + ' by adding offsetParent ' + parent._offsetParent.tagName + ' border start = ' + parent._offsetParent._borderStart[dir]);
parent._offsetStart[dir] += parent._offsetParent._borderStart[dir];
}
//if(dir==testdir)console.debug('subtracting border start = ' + 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._offsetStart[dir] - parent._scrolledAmount[dir] - (relative? 0 : parent._offsetStart[dir]) - parent._borderStart[dir];
// if overflow is positive, number of pixels obscured by the parent's end
var overflow = underflow + element._offsetSize[dir] - parent._offsetSize[dir] + parent._borderSize[dir];
//if(dir==testdir)console.debug('element = ' + element.tagName + ', offsetStart = ' + element._offsetStart[dir] + ', relative = ' + relative + ', parent offsetStart = ' + parent._offsetStart[dir] + ', scroll = ' + parent._scrolledAmount[dir] + ', parent border start = ' + parent._borderStart[dir] + ', parent border size = ' + parent._borderSize[dir] + ', underflow = ' + underflow + ', overflow = ' + overflow + ', element offsetSize = ' + element._offsetSize[dir] + ', parent offsetSize = ' + parent._offsetSize[dir]);
var 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;
// don't scroll if the over/underflow signs are opposite since that means that
// the node extends beyond parent's boundary in both/neither directions
var scrollAmount = (underflowScroll*overflowScroll <= 0)? 0 : Math[(underflowScroll < 0)? "max" : "min"](underflowScroll, overflowScroll);
//if(dir==testdir)console.debug('element = ' + element.tagName + ' dir = ' + dir + ', scrollAmount = ' + scrollAmount);
if(scrollAmount != 0){
var oldScroll = parent[scrollAttr];
parent[scrollAttr] += (reverse)? -scrollAmount : scrollAmount; // actually perform the scroll
var scrolledAmount = parent[scrollAttr] - oldScroll; // in case the scroll failed
//if(dir==testdir)console.debug('scrolledAmount = ' + scrolledAmount);
}
if(relative){
element._offsetStart[dir] += parent._offsetStart[dir];
}
element._offsetStart[dir] -= parent[scrollAttr];
}
element._parent = parent._parent;
element._offsetParent = parent._offsetParent;
}
parent = node;
var next;
while(parent && parent.removeAttribute){
next = parent.parentNode;
parent.removeAttribute('_offsetParent');
parent.removeAttribute('_parent');
parent = next;
}
}catch(error){
console.error('scrollIntoView: ' + error);
node.scrollIntoView(false);
}
};