// keep the global scope clean (function() { var id = null; var warnings = null; var continueBtn = null; var getElementPosY = function(obj) { var y = 0; while (obj.offsetParent) { y += obj.offsetTop; obj = obj.offsetParent; } return y; }; // ugh, find scroll position in 3 possible ways var getScrollY = function() { return self.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; }; var initScroll = function(obj) { // if we scroll to the warning div itself the sql that caused the warning will be off the top of the page // so we can look through the page for a preceeding div and base the scroll position on that var prevDiv = findPreviousSibling(obj, 'div'); // if the warning div doesn't have a previous sibling div scroll to the top of the page instead var y = (prevDiv) ? getElementPosY(prevDiv) + prevDiv.offsetHeight : 0; if (id) { clearInterval(id); } // scroll with a little bit of easing, I guess it's a matter of personal taste whether it would be // better to scroll the page directly to the point using window.scrollTo(0, y). But I think easing // makes it a little clearer to the user that they're scrolling through the page :-) id = setInterval(function() { var ys = getScrollY(); // the stuff on arguments.callee is a check to stop scrolling if we've reached the end of the page // and can't scroll any further if ((arguments.callee.oldPos && arguments.callee.oldPos == ys) || Math.abs(y - ys) < 5) { arguments.callee.oldPos = null; window.scrollTo(0, y); clearInterval(id); id = null; } else { window.scrollTo(0, Math.round(ys + ((y - ys) / 2))); } arguments.callee.oldPos = ys; }, 60); }; // return nodes with a class name that matches regexp - if individual is set we're only looking // for a single node var filterNodesByClassName = function(nodes, regexp, individual) { var filtered = []; var n = nodes.length; for (var i = 0; i < n; ++i) { if (nodes[i].className && nodes[i].className.match(regexp)) { if (individual) { return nodes[i]; } filtered.push(nodes[i]); } } return filtered; }; // look through the previous siblings of an element and find the first one with a given node name var findPreviousSibling = function(obj, nodeName) { while (obj = obj.previousSibling) { if (obj.nodeName.toLowerCase() == nodeName) { return obj; } } return false; }; // create the links to scroll around the page. warningIndex is used to look up the element in the // warnings array that should be scrolled to var createWarningSkipLink = function(linkText, warningIndex, style) { var link = document.createElement('a'); link.href = 'javascript:;'; link.warningIndex = warningIndex; link.appendChild(document.createTextNode(linkText)); link.onclick = function() { initScroll(warnings[this.warningIndex]); }; if (style) { for (var x in style) { link.style[x] = style[x]; } } return link; }; var checkWarnings = function() { // look for div tags with the class name notifyproblem or error box warnings = filterNodesByClassName(document.getElementsByTagName('div'), /((^|\b)notifyproblem(\b|$))|errorbox/); // and find the continue button continueBtn = filterNodesByClassName(document.getElementsByTagName('div'), /(^|\b)continuebutton(\b|$)/, true); var n = warnings.length; // find how many warnings warnings[warnings.length] = continueBtn; // then add the continue button to the array var link; var statusOk = false; for (var i = 0; i < n; ++i) { // add a "next" link to all warnings except the last one on the page if (i < n - 1) { link = createWarningSkipLink('Scroll to next warning', i + 1, {paddingLeft: '1em'}); } else { // on the last link add a link to go to the continue button link = createWarningSkipLink('Scroll to continue button', i + 1, {paddingLeft: '1em'}); } warnings[i].appendChild(link); // and add a "previous" link to all except the first if (i > 0) { link = createWarningSkipLink('Scroll to previous warning', i - 1, {paddingRight: '1em'}); warnings[i].insertBefore(link, warnings[i].firstChild); } } var contentDiv = document.getElementById('content'); if (contentDiv) { // create a message to display at the top of the page, with a link to the first warning // or to the continue button if there were no warnings on the page var p = document.createElement('p'); if (n > 0) { var warningText = (n == 1) ? 'warning' : 'warnings'; link = createWarningSkipLink('Scroll to the first warning', 0); p.appendChild(document.createTextNode('This script generated ' + n + ' ' + warningText + ' - ')); p.className = 'notifyproblem'; } else { link = createWarningSkipLink('Scroll to the continue button', 0); p.appendChild(document.createTextNode('No warnings - ')); p.className = 'notifysuccess'; statusOk = true; } p.appendChild(link); contentDiv.insertBefore(p, contentDiv.firstChild); } // automatically scroll to the first warning or continue button initScroll(warnings[0]); }; // load should be a document event, but most browsers use window if (window.addEventListener) { window.addEventListener('load', checkWarnings, false); } else if (document.addEventListener) { document.addEventListener('load', checkWarnings, false); } else if (window.attachEvent) { // sometimes IE doesn't report scrollTop correctly (it might be a quirk of this specific page, I don't know) // but using scrollTo once to begin makes sure things work window.scrollTo(0, 1); window.attachEvent('onload', checkWarnings); } })();