/* ------------------------------------------------------------------------ * * SAS Institute, Inc. * * Copyright (c) 2005 Institute, Inc. All rights reserved. * * Purpose: Contains common javascript functionality meant to be * reused in order to perform certain functions consistently * across CDD space. * * Ideally this common functionality should be compatible across * major browsers. * * ------------------------------------------------------------------------ */ // Determine Browser // xOp7 = Opera v7, xOp5or6= Opera v5 or v6 // xIE = Internet Explorer, xNN4 = Netscape 4 xOp7=false,xOp5or6=false,xIE=false,xNN4=false; var xUA=navigator.userAgent.toLowerCase(); if(window.opera){ xOp7=(xUA.indexOf('opera 7')!=-1 || xUA.indexOf('opera/7')!=-1); if (!xOp7) xOp5or6=(xUA.indexOf('opera 5')!=-1 || xUA.indexOf('opera/5')!=-1 || xUA.indexOf('opera 6')!=-1 || xUA.indexOf('opera/6')!=-1); } else if(document.layers) xNN4=true; else {xIE=document.all && xUA.indexOf('msie')!=-1 && parseInt(navigator.appVersion)>=4;} /* ------------------------------------------------------------- * * Get the current style for an element in browser * independant fashion. IE uses currentStyle while Mozilla * currently uses window.getComputedStyle(...) * ------------------------------------------------------------- */ function sas_getCurrentStyle(element, style) { var styleValue=""; if(element!=null) { if (document.defaultView && document.defaultView.getComputedStyle) { styleValue=document.defaultView.getComputedStyle(element,null).getPropertyValue(style); } else { if(element.currentStyle) { styleValue=element.currentStyle[style]; } } } return styleValue; } /* ------------------------------------------------------------- * * --Begin createXMLHttpRequest-- * * Attempt to create an xmlhttprequest for communicating * with server * ------------------------------------------------------------- */ function sas_createXMLHttpRequest() { var xmlhttp; if(xIE) { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // IE } else { xmlhttp = new XMLHttpRequest(); } return xmlhttp; } /* ------------------------------------------------------------- * * --Begin sas_registerEventHandler-- * * Attempt to register an event listener in cross browser * fashion. * * Parm: element - object on which to register the handler. * Parm: eventType - event type to register this handler for. * don't use 'on' prefix as this will be added * automatically. 'click' rather than 'onclick'. * Parm: listener - the function to register as the handler. * Parm: capture - specifies whether the handler should capture events. * * Example Useage... * sas_registerEventHandler(document, 'click', handleClick, false); * * ------------------------------------------------------------- */ function sas_registerEventHandler ( element, eventType, listener, capture ) { if (element != null && eventType!=null && listener!=null) { if( element.attachEvent != null) // IE way { element.attachEvent( "on" + eventType, listener); } else { element.addEventListener( eventType, listener, capture ); } } } /* ------------------------------------------------------------- * * --Begin sas_unRegisterEventHandler-- * * Attempt to remove an event listener in cross browser * fashion. * * Parm: element - object on which to unregister event handler * Parm: eventType - event type to unregister this handler for. * don't use 'on' prefix as this will be added * automatically. 'click' rather than 'onclick'. * Parm: listener - the function to unregister as the handler. * Parm: capture - specifies whether the handler being removed * captures events. * * Example Useage... * sas_unRegisterEventHandler(document, 'click', handleClick, false); * * ------------------------------------------------------------- */ function sas_unRegisterEventHandler ( element, eventType, listener, capture ) { if (element != null && eventType!=null && listener!=null) { if( element.attachEvent != null) // IE way { element.detachEvent( "on" + eventType, listener); } else { element.removeEventListener( eventType, listener, capture ); } } } /* ------------------------------------------------------------- * * --Begin sas_autoPosition-- * * Attempt to automatically do an absolute positioning on an html * element relative to some other element. Will attempt several * predefined relative positions until it finds one that doesn't * cause the element to be clipped. If none of the predefined * positions work without clipping then we just dump the element * anywhere. * * Example Useage... * relativeToElement = document.getElementById("MyImageId"); * DivToPosition = document.getELementById("MyPopupMenu"); * sas_autoPosition(DivToPosition, relativeToELement); * * ------------------------------------------------------------- */ function sas_autoPosition(elementToPosition, relativeToElement) { var relativeToCoordinates = new sas_Coordinates(relativeToElement); relativeToCoordinates.setRelativeToSharedBreakoutBox(elementToPosition); // lets try several positions which we would consider ideal and // if none of them work without clipping then we'll have to resort to // just dumping it somewhere even though it does clip //sas_log_println('-----Start sas_autoPosition-------'); // 1. try positioning elementToPosition left edge aligned with left edge // of relativeToElement and top edge aligned with bottomtop edge of relativeToElement. proposedLeft = relativeToCoordinates.getX(); proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight(); //sas_log_println('-----Attempt #1-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 2. try positioning element right edge aligned with right edge // of relativeToElement and top edge aligned with bottom edge of relativeToElement proposedLeft = relativeToCoordinates.getX() - (elementToPosition.offsetWidth - relativeToCoordinates.getWidth()); proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight(); //sas_log_println('-----Attempt #2-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 3. try positioning elementToPosition left edge aligned with left edge of // relativeToElement area and bottom edge aligned with top edge of relativeToElement proposedLeft = relativeToCoordinates.getX(); proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight; //sas_log_println('-----Attempt #3-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 3. try positioning elementToPosition right edge aligned with right edge // of relativeToElement image and bottom edge aligned with top edge of relativeToElement proposedLeft = relativeToCoordinates.getX() - (elementToPosition.offsetWidth - relativeToCoordinates.getWidth()); proposedTop = relativeToCoordinates.getY() - elementToPosition.offsetHeight; //sas_log_println('-----Attempt #4-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 5. try positioning elementToPosition right edge aligned with left edge // of relativeToElement and center aligned with center of relativeToElement proposedLeft = relativeToCoordinates.getX() - elementToPosition.offsetWidth; proposedTop = relativeToCoordinates.getY() - (elementToPosition.offsetHeight/2) + (relativeToCoordinates.getWidth()/2); //sas_log_println('-----Attempt #5-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 6. try positioning elementToPosition left edge aligned with right edge // of relativeToElement and center aligned with center of relativeToElement proposedLeft = relativeToCoordinates.getX() + relativeToCoordinates.getWidth(); proposedTop = relativeToCoordinates.getY() - (elementToPosition.offsetHeight/2) + (relativeToCoordinates.getWidth()/2); //sas_log_println('-----Attempt #6-------'); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 7. last ditch effort... Could probably write another attempt that tries to figure out how much space is available // on each side of the relativeToELement and optimize where the elementToPosition goes for the last try. // Just dump it at our ideal spot (#1) even though it will clip proposedLeft = relativeToCoordinates.getX(); proposedTop = relativeToCoordinates.getY() + relativeToCoordinates.getHeight(); //sas_log_println('-----Attempt #7 (Last Ditch)-------'); } } } } } } elementToPosition.style.left = proposedLeft; elementToPosition.style.top = proposedTop; //sas_log_println('-----End sas_autoPosition-------'); } /* ------------------------------------------------------------- * * --End sas_autoPosition-- * ------------------------------------------------------------- */ /* ------------------------------------------------------------- * * --Begin sas_autoPositionHorizontally-- * * Attempt to automatically do an absolute positioning on an html * element relative to some other element. This version * is meant for items which have a preference of positioning * horizontally relative to an item. Such items include * submenus or popups off of vertical menus. * If we can't find suitable horizontal alignment then we just * run through as if it weren't a horizontal preference until * we hit a good position or give up. * * Example Useage... * relativeToElement = document.getElementById("MyImageId"); * DivToPosition = document.getELementById("MyPopupMenu"); * sas_autoPositionHorizontally(DivToPosition, relativeToELement); * * ------------------------------------------------------------- */ function sas_autoPositionHorizontally(elementToPosition, relativeToElement) { var relativeToCoordinates = new sas_Coordinates(relativeToElement); relativeToCoordinates.setRelativeToSharedBreakoutBox(elementToPosition); // lets try several positions which we would consider ideal and // if none of them work without clipping then we'll have to resort to // just dumping it somewhere (0,0)?? // 1. try positioning elementToPosition left edge indented from right edge // of relativeToElement and top edge aligned with top edge of relativeToElement. proposedLeft = relativeToCoordinates.getX() + relativeToCoordinates.getWidth(); proposedTop = relativeToCoordinates.getY(); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // 2. try positioning element right edge indented from left edge // of relativeToElement and top edge aligned with top edge of relativeToElement proposedLeft = relativeToCoordinates.getX() - elementToPosition.offsetWidth; proposedTop = relativeToCoordinates.getY(); if(sas_checkClipping(elementToPosition, proposedLeft, proposedTop)==true) { // if the preferred submenu alignments didn't work go through // the position posibilities for non submenus. sas_autoPosition(elementToPosition, relativeToElement); return; } } elementToPosition.style.left = proposedLeft; elementToPosition.style.top = proposedTop; } /* ------------------------------------------------------------- * * --End sas_autoPositionHorizontal-- * ------------------------------------------------------------- */ /* ------------------------------------------------------------- * * --Begin sas_checkClipping-- * * js function to try and determine whether placing a given html element * at a specified set of absolute coordinates would cause the element * to be clipped. * * Example Useage... * if(sas_checkClipping(palette, proposedLeft, proposedTop)==true) * this would clip so figure out somewhere else to place it. * * ------------------------------------------------------------- */ function sas_checkClipping(element, proposedLeft, proposedTop) { var clip=false; elementWidth = element.offsetWidth; elementHeight = element.offsetHeight; windowWidth = document.body.clientWidth; windowHeight = document.body.clientHeight; // proposedLeft and proposedTop coming in are likely relative // to elements breaking box. For clipping check we need to // convert proposedLeft and proposedTop to be relative to the // entire page. so.. Get breaking box of element. and then // get that breaking boxes coordinates relative to the entire // document. add that height and width to proposedTop and proposedLeft breakoutBox = sas_getBreakoutBox(element); if(breakoutBox!=null) { // Note: the above statement about clipping should be checked // relative to entire page is generally true unless element // lives in a breaking box that can itself cut off content such // as overflows of clip, scroll, etc... so need to not adjust // to entire page if this is the case. if(sas_getCurrentStyle(breakoutBox,'overflowY')!='auto') // if(breakoutBox.currentStyle.overflowY!='auto') { breakoutBoxCoordinates = new sas_Coordinates(breakoutBox); breakoutBoxCoordinates.setRelativeToPage(); proposedLeft += breakoutBoxCoordinates.getX(); proposedTop += breakoutBoxCoordinates.getY(); } } // if element lives in scrollable overflow area then // proposedLeft and proposedTop are likely relative // to the virtual area (included nonvisible scroll areas) // in checking clipping it needs to be going against // the physical area instead so adjust them as needed. if (element != null) { parentComponent = element; while(parentComponent!=null) { //*** Note: the check for scrollTop>0, scrollLeft>0 deals with any scrollable area // that is currently scrolled including the main browser window itself. // The subsequent check for overflow settings specifically catches scroll areas OTHER // than the main browser window itself. // alter proposedTop and proposedLeft to take into account scrolling space. // this will treat the proposed top relative to the physical // top/left of the overflow area instead of relative to the virtual // top/left of the overflow area if(parentComponent.scrollTop>0 || parentComponent.scrollLeft>0) { proposedTop = proposedTop - parentComponent.scrollTop; proposedLeft = proposedLeft - parentComponent.scrollLeft; } // if the scrollable area is something other than body then // we need to alter the window width and height used as // clipping checks to be the internal scrollable area // instead of the entire window. // settings other than auto creating scrollable areas? if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto' || sas_getCurrentStyle(parentComponent,'overflowX')=='auto') { windowWidth=parentComponent.offsetWidth; windowHeight=parentComponent.offsetHeight; break; } parentComponent = parentComponent.offsetParent; } } // will it clip on left? if(proposedLeft<0) clip = true; // will it clip on top? if(proposedTop<0) clip = true; // will it clip on right side? if(proposedLeft+elementWidth>windowWidth) clip = true; // will it clip on bottom if(proposedTop+elementHeight>windowHeight) clip = true; //if(clip) // sas_log_println('INVALID!! Proposed=[' +proposedLeft + ', ' + proposedTop + '], Dimensions=['+elementWidth+', '+elementHeight+'], WindowDimensions=[' + windowWidth + ', ' + windowHeight + ']'); //else // sas_log_println('Valid Proposed=[' +proposedLeft + ', ' + proposedTop + '], Dimensions=['+elementWidth+', '+elementHeight+'], WindowDimensions=[' + windowWidth + ', ' + windowHeight + ']'); return clip; } /* ------------------------------------------------------------- * * --End sas_checkClipping-- * ------------------------------------------------------------- */ /* ------------------------------------------------------------- * * --Begin sas_Coordinates-- * * js object for determining absolute coordinates of an * html element. Useful for positioning one element relative * to another with absolute position such as popup menus, * Calendar popup, Color Picker popup, etc... * * * * NOTE: * Depending on what you plan on doing with the coordinates of a given element you may want either * coordinates relative to the top/left of the page OR coordinates relative to the top/left of the * nearest relatively positioned containing box(breakout box). * * This is because an absolutely positioned element withing a breakoutbox (SPAN, DIV, etc..) that * has a position of relative or absolute uses coordinates relative to that breakoutbox rather than to the the page. * breakoutbox html elements that this is true for seem to differ based on browser. In IE it appears to * be supported fairly widely across the board. In Mozilla it seems more restricted to things like Spans, Divs * and not supported for things like Table, TH, TD, etc... * * Adding third case where you want coordinates of element relative to a breakoutbox for a different * element. Take a popup div(p) which you want to position relative to an image (i). If p lives inside of * different breakoutbox than i then the coordinates of i relative to page or its own breakoutbox do * you know good. In this case you really need to the coordinates of i relative to the first breakoutbox * that both i and p have in common. * * * default coordinates will be relative to parent breakoutbox. * * Example Useage getting coordinates relative to parent breakoutbox. * var coords = new sas_Coordinates(document.getElementById("someelement")); * coords.getX() * coords.getY() * coords.getWidth() * coords.getHeight() * * Example Useage getting coordinates relative to page. * var coords = new sas_Coordinates(document.getElementById("someelement")); * coords.setRelativeToPage(); * * Example Useage getting coordinates relative to first breakoutbox shared with * some other element. * var coords = new sas_Coordinates(document.getElementById("someimage")); * coords.setRelativeToSharedBreakoutBox("menudiv"); * * Parameters: * element - the html element you want to get coordinates for * * ------------------------------------------------------------- */ function sas_Coordinates(element) { this.element=element; // determine absolute coordinates of the element elementLeft=0; elementTop=0; if (element != null) { parentComponent = element; while(parentComponent!=null) { // need to deal with horizontal overflows as well and // also deal with anytime it creates a separate container box. // Settings other than auto could trigger this such as "scroll". if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto') // if(parentComponent.currentStyle.overflowY=="auto") break; // as we walk up the chain if we find an appropriate item with relative positioning then // don't count it and stop immediately so that we return coordinates relative // to this relative containing box if(sas_getCurrentStyle(parentComponent,'position')=='relative' || sas_getCurrentStyle(parentComponent,'position')=='absolute') { // IE elements which honor 'relative' position if(xIE) break; // Mozilla elements which honor 'relative' position if(parentComponent.tagName=='SPAN' || parentComponent.tagName=='DIV') break; } elementLeft = elementLeft + parentComponent.offsetLeft; elementTop = elementTop + parentComponent.offsetTop; parentComponent = parentComponent.offsetParent; } } this.x=elementLeft; this.y=elementTop; this.init(); return this; } function sas_Coordinates_init() { if(xIE) { this.width=this.element.offsetWidth; this.height=this.element.offsetHeight; } else { this.width=this.element.offsetWidth; this.height=this.element.offsetHeight; } } function sas_Coordinates_getX() { return this.x; } function sas_Coordinates_getY() { return this.y; } function sas_Coordinates_getWidth() { return this.width; } function sas_Coordinates_getHeight() { return this.height; } // comment the caveaut function sas_Coordinates_setRelativeToSharedBreakoutBox(breakoutBoxChild) { // determine absolute coordinates of the element elementLeft=0; elementTop=0; breakoutBox = sas_getBreakoutBox(breakoutBoxChild); if (this.element != null) { parentComponent = this.element; while(parentComponent!=null && parentComponent!=breakoutBox) { elementLeft = elementLeft + parentComponent.offsetLeft; elementTop = elementTop + parentComponent.offsetTop; parentComponent = parentComponent.offsetParent; } } this.x=elementLeft; this.y=elementTop; this.init(); } function sas_Coordinates_setRelativeToPage() { // determine absolute coordinates of the element elementLeft=0; elementTop=0; if (this.element != null) { parentComponent = this.element; while(parentComponent!=null) { elementLeft = elementLeft + parentComponent.offsetLeft; elementTop = elementTop + parentComponent.offsetTop; parentComponent = parentComponent.offsetParent; } } this.x=elementLeft; this.y=elementTop; this.init(); } sas_Coordinates.prototype.getX=sas_Coordinates_getX; sas_Coordinates.prototype.getY=sas_Coordinates_getY; sas_Coordinates.prototype.getWidth=sas_Coordinates_getWidth; sas_Coordinates.prototype.getHeight=sas_Coordinates_getHeight; sas_Coordinates.prototype.setRelativeToSharedBreakoutBox=sas_Coordinates_setRelativeToSharedBreakoutBox; sas_Coordinates.prototype.setRelativeToPage=sas_Coordinates_setRelativeToPage; sas_Coordinates.prototype.init=sas_Coordinates_init; /* ------------------------------------------------------------- * * --End sas_Coordinates-- * ------------------------------------------------------------- */ /* ------------------------------------------------------------- * * find the container box (breakout box) for this element. * This will walk up the dom tree until it finds an ancestor * which causes absolute child coordinates to be relative to * the box. This could walk all the way back up to the root * document. * ------------------------------------------------------------- */ function sas_getBreakoutBox(element) { if (element != null) { parentComponent = element; while(parentComponent.offsetParent!=null) { parentComponent = parentComponent.offsetParent; if(sas_getCurrentStyle(parentComponent,'overflowY')=='auto') // if(parentComponent.currentStyle.overflowY=="auto") break; if(sas_getCurrentStyle(parentComponent,'position')=='relative' || sas_getCurrentStyle(parentComponent,'position')=='absolute') { // IE elements which honor 'relative' position if(xIE) break; // Mozilla elements which honor 'relative' position if(parentComponent.tagName=='SPAN' || parentComponent.tagName=='DIV') break; } } } return parentComponent; } /* ------------------------------------------------------------- * * --Begin Javascript logging helpers-- * * js functions for helping log from javascript without using * alerts. This will open a separate window and dump all log * messages to this window instead of opening alert dialog. * * Example Useage: * * // print a simple debug message * sas_log_println("Hey I'm logging a message"); * * // print all the elements and their values for a form. * sas_log_printFormElements(someform); * * // print all the properties for a given element. * sas_log_printObjectProperties(somediv, "Popup_Div"); * * ------------------------------------------------------------- */ var sas_log = null; //handle to log window var sas_logging_enabled=true; //to log or not to log... that is the question! function sas_log_enable(enable) { if (sas_logging_enabled != enable) { sas_logging_enabled = enable; } } function sas_log_println(msg) { if(sas_logging_enabled==true) { sas_log_getLog().document.write(msg, "
"); sas_log_getLog().scrollBy(0,1000000); //scroll to the end window.status = msg; } } function sas_log_printFormElements(form) { if(sas_logging_enabled==true) { var text = "Form["+form.name+"] Elements:
\n"; if (form != null && form.elements == null) { //must be the ID for the form try to find the real form object form = document.getElementById(form); } if (form != null && form.elements != null) { var elText; for (i=0; i\n"; } } if (text.length > 0) { sas_log_println(text); } } } function sas_log_printObjectProperties(obj, obj_name) { if(sas_logging_enabled==true) { var result = "Object["+obj_name+"] Properties:
\n"; var props = new Array(); for (var i in obj) { props[props.length] = i; } props.sort(); for (var i =0; inull
\n"; else if (v.replace) { v = v.replace("<", "<"); v = v.replace(">", ">"); } result += v + "
\n"; } catch (e) { result += "ERROR: "+e.message+"
\n"; } } sas_log_println(result); } } function sas_log_getLog() { if (sas_log == null || sas_log.closed == true) { sas_log = window.open("", "sas_log", "width=600,height=500,left=0px,top=0px,resizable,scrollbars=yes,status=0"); // sas_log.document.open("text/plain"); //set document type to plain text? sas_log.document.bgcolor = "#FFCC00"; sas_log.document.title = "SAS JavaScript Log"; } return sas_log; } /* ------------------------------------------------------------- * * --End Javascript logging helpers-- * ------------------------------------------------------------- */ /* ------------------------------------------------------------- * * emulate parent.contains(child) * ------------------------------------------------------------- */ function sas_DOMContains( parent, child ) { if (parent.contains) return parent.contains(child); if (parent==child) return false; while (child !=null && child != parent) child = child.parentNode; return child != null; }