/* * Document Object Model (DOM) Information functions. */ /** These routines require the following scripts to be loaded before this script file. These functions provide information of document elements. // The following are for version 5 browsers. function getEventElementLeft(evt,target_e ,border_width,padding_width) { function getEventElementTop(evt,target_e ,border_width,padding_width) { // The following are for version 6+ browsers. They don't need the mouse // coordinates as earlier browsers do in order to find the coordinates. function getElementLeft(e) function getElementTop(e) function getElementStyle(e, dom_prop, css_attr, dom_gen_prop, css_gen_attr, position, dom_border_prop, css_border_attr, component, dom_border_gen_prop, css_border_gen_attr) function getBorderWidth(e,position) function getPadding(e,position) function getMargin(e,position) function getElementWidth(e) function getElementHeight(e) * Utility functions function parseNum(s) // base 10, no NaN function getElementRef(id) // id can be a string or object The "position" argument above can be one of four constants: left right top bottom The getElementStyle() will investigate CSS style sheets if needed. These functions are copies of those developed for AcmeMenu. **/ function getEventElementLeft(evt,target_e ,border_width,padding_width) { // Get the left (x) coordinate of target element. // The coordinate includes padding and border but not the margin. // The specified target element can be a container element to the // element actually receiving the event. // The border_width argument is used only for IE4 or IE5. // The padding_width argument is used only for IE5Mac. // They allow constants to be used for specifying target border size // and padding. They are computed if missing. // Theoretically this routine should not be necessary to find // an element's coordinates, but some cases still use the mouse // coordinates to find an element's coordinate. if (! evt) evt = window.event; // IE default value if (! target_e) target_e = evt.target ? evt.target : evt.srcElement ? evt.srcElement : null; target_e = getElementRef(target_e); // can be string or object var x; // returned coordinate var e, ox; var target_offset = 0; if (bv.isIE5 || bv.isOpr5) { if (border_width == null) border_width = getBorderWidth(target_e,"left"); // Get element initially receiving the event. e = evt.target ? evt.target : evt.srcElement ? evt.srcElement : null; debug("getEventElementLeft(): IE5: begin evt.offsetX="+evt.offsetX+ ", border_width="+border_width,e); // Check for the case of: // Unless a width has been specified for an tag, it is not // in the chain of nodes traversed by the offsetParent link. if (! e.nodeName || e.nodeName != "IMG" || ! e.parentNode || e.parentNode.nodeName != "A" || ! e.offsetParent || e.offsetParent.nodeName == "A") { while (e) { // Add offset between event element and target element. if (e == target_e) break; debug("getEventElementLeft(): IE5: adding to"+ " target_offset "+e.offsetLeft+ ", target_e="+target_e.nodeName ,e); target_offset += e.offsetLeft; e = e.offsetParent; } } target_offset += border_width; x = evt.clientX - evt.offsetX - target_offset; if (bv.isIE5) x += document.body.scrollLeft; debug("getEventElementLeft(): IE5"+ " Computed x="+x+ ", (evt.offsetX="+evt.offsetX+ ", evt.clientX="+evt.clientX+ ", scrollLeft="+document.body.scrollLeft+ ", target_offset="+target_offset+ ", border_width="+border_width+ ", padding_width="+padding_width+ ")",target_e); return x; } else if (bv.isIE5Mac) { if (border_width == null) border_width = getBorderWidth(e,"left"); if (padding_width == null) padding_width = getPadding(e,"left"); // Get element initially receiving the event. e = evt.target ? evt.target : evt.srcElement ? evt.srcElement : null; debug("getEventElementLeft(): IE5Mac: begin "+ ", padding_width="+padding_width+", border="+border_width,e); // Check for the case of: if (e.nodeName && e.nodeName == "IMG" && e.parentNode && e.parentNode.nodeName == "A" && e.offsetParent && e.offsetParent.nodeName != "A") { x = evt.clientX - evt.offsetX - border_width - padding_width; debug("getEventElementLeft(): IE5Mac:"+ " Computed x="+x+", (ox="+ox+ ", evt.clientX="+evt.clientX+ ", evt.offsetX="+evt.offsetX+ ", border_width="+border_width+ ", padding_width="+padding_width+ ")",target_e); } else { ox = 0; target_found = false; while (e) { debug("getEventElementLeft(): IE5Mac: adding "+e.offsetLeft,e); ox += e.offsetLeft; if (e == target_e) target_found = true; // Add offset between event element and target element. if (! target_found) { target_offset += e.offsetLeft; } e = e.offsetParent; } target_offset += border_width + padding_width; x = getMargin(document.body,"left") + ox - target_offset; debug("getEventElementLeft(): IE5Mac:"+ " Computed x="+x+", (ox="+ox+ ", border_width="+border_width+ ", padding_width="+padding_width+ ", target_offset="+target_offset+ ")",target_e); } return x; } else if (bv.isOpr6) { x = getElementLeft(target_e); debug("getEventElementLeft(): Opr6: Computed x="+x); return x; } else { // (bv.isIE6 || bv.isNS || bv.isMoz || bv.isOpr7 || bv.isSafari) // Ignore TEXT nodes, go up to first ELEMENT node. e = target_e; while (e && e.nodeType != 1) { e = e.parentNode }; x = getElementLeft(e); debug("getEventElementLeft(): DOM: Computed x="+x); return x; } return 0; // cannot determine coordinate } function getEventElementTop(evt,target_e ,border_width,padding_width) { // Get the top (y) coordinate of target element. // The coordinate includes padding and border but not the margin. // The specified target element can be a container element to the // element actually receiving the event. // All arguments are optional except evt in Netscape (DOM). // The border_width argument is used only for IE4 or IE5. // The padding_width argument is used only for IE5Mac. // They allow constants to be used for specifying target border size // and padding. They are computed if missing. // Like getEventElementLeft(), this routine should not be // necessary, but mouse coordinates are still used to compute an // element's coordinate in older browsers. // Note: with the exception of Internet Explorer, the target element // must be a block type element (not inline) otherwise the Y // coordinate is incorrect. It is based on the text size, and if // the element is an tag enclosing an image, it is incorrect. if (! evt) evt = window.event; // IE default value if (! target_e) target_e = evt.target ? evt.target : evt.srcElement ? evt.srcElement : null; target_e = getElementRef(target_e); // can be string or object debug("getEventElementTop(): target_e is",target_e); var y; // returned coordinate var e, oy; var target_offset = 0; if (bv.isIE5 || bv.isIE5Mac || bv.isOpr5) { if (border_width == null) border_width = getBorderWidth(target_e,"top"); if (bv.isIE5Mac && padding_width == null) padding_width = getPadding(target_e,"top"); // Get element initially receiving the event. e = evt.target ? evt.target : evt.srcElement ? evt.srcElement : null; debug("getEventElementTop(): IE5: begin evt.offsetY="+evt.offsetY+ ", border_width="+border_width,e); // Get offset within element receiving the event. oy = evt.offsetY; // Check for the case of: // Unless a width has been specified for an tag, it is not // in the chain of nodes traversed by the offsetParent link. // This also applies to Opera 5 but e.nodeName is undefined // so we cannot check it or correct it. if (! e.nodeName || e.nodeName != "IMG" || ! e.parentNode || e.parentNode.nodeName != "A" || ! e.offsetParent || e.offsetParent.nodeName == "A") { while (e) { // Add offset between event element and target element. if (e == target_e) break; debug("getEventElementTop(): IE5: adding to"+ " target_offset "+e.offsetTop+ ", target="+target_e.nodeName ,e); target_offset += e.offsetTop; e = e.offsetParent; } } target_offset += border_width; if (bv.isIE5Mac) target_offset += padding_width; y = evt.clientY - oy - target_offset; if (bv.isIE5) y += document.body.scrollTop; debug("getEventElementTop(): IE5:"+ " Computed y="+y+ ", (oy="+oy+ ", scrollTop="+document.body.scrollTop+ ", evt.clientY="+evt.clientY+ ", target_offset="+target_offset+ ", border_width="+border_width+ ")",target_e); return y; } else if (bv.isOpr6) { y = getElementTop(target_e) + Menu.BodyMarginTop; debug("getEventElementTop(): Opr6: Computed y="+y); return y; } else { // (bv.isIE6 || bv.isNS || bv.isMoz || bv.isOpr7 || bv.isSafari) // Ignore TEXT nodes, go up to first ELEMENT node. e = target_e; while (e && e.nodeType != 1) { e = e.parentNode }; y = getElementTop(e); debug("getEventElementTop(): DOM: Computed y="+y); return y; } return 0; // cannot determine coordinate } function getElementLeft(e) { // Get the left (x) coordinate of an element. // The coordinate includes padding and border but not the margin. // This only works with version 6 browsers (IE6, NS6, Opr7). debug("getElementLeft(): begin e.offsetLeft="+e.offsetLeft,e); var x = e.offsetLeft; while ((e = e.offsetParent)) { debug("getElementLeft(): adding e.offsetLeft="+e.offsetLeft+ " and left border.",e); x += e.offsetLeft; if (bv.isSafari || bv.isKonq) { // Positioned elements should exclude body margins if (getElementStyle(e,'position') == 'absolute') break; } else { if (! ((bv.isNS71 || bv.isIE6 || bv.isMoz) && e.nodeName == 'TABLE') ) { x += getBorderWidth(e,"left"); } } // Note: Netscape adds an extra 1px to the border between // cells of tables that have a border defined. It gets // returned in the getBorderWidth() for the TD or TH // element when stepping through offsetParent nodes. // Internet Explorer 6 doesn't include its added pixel // in getBorderWidth(), so it needs to add that extra pixel. if (e.border && parseNum(e.border) > 0) { if (bv.isNS || bv.isMoz) x--; if (bv.isIE6 || bv.isOpr7) x++; } } debug("getElementLeft(): Computed x="+x,e); return x; } function getElementTop(e) { // Get the top (y) coordinate of an element. // The coordinate includes padding and border but not the margin. // This only works with version 6 browsers (IE6, NS6, Opr7). debug("getElementTop(): begin e.offsetTop="+e.offsetTop,e); var y = e.offsetTop; while ((e = e.offsetParent)) { debug("getElementTop(): adding e.offsetTop="+e.offsetTop+ " and top border.",e); y += e.offsetTop; if (bv.isSafari || bv.isKonq) { // Positioned elements should exclude body margins. if (getElementStyle(e,'position') == 'absolute') break; } else { if (! ((bv.isNS71 || bv.isIE6 || bv.isMoz) && e.nodeName == 'TABLE') ) { y += getBorderWidth(e,"top"); } } // Note: Netscape adds an extra 1px to the border between // cells of tables that have a border defined, and one on top. if (parseNum(e.border) > 0) { if (bv.isNS || bv.isMoz) y--; if (bv.isIE6 || bv.isOpr7) y++; } } debug("getElementTop(): Computed y="+y,e); return y; } function getElementStyle(e, dom_prop, css_attr, dom_gen_prop, css_gen_attr, position, dom_border_prop, css_border_attr, component, dom_border_gen_prop, css_border_gen_attr) { // Get the element's style settings. // Both the DOM property names (e.g. borderBottomWidth) and the // corresponding CSS attribute names (border-bottom-width) // needs to be specified. If css_attr not set, it is set to dom_prop. // // General properties and attributes for dom_prop and css_attr // are checked if specified. For example: a general property dom_gen_prop // for borderBottomWidth is borderWidth, and the css_gen_attr is // "border-width". The position argument can be one of: // "top", "bottom", "left", or "right". // // The dom_border_gen_prop and css_border_gen_attr are for the // special shortcut method of specifying borders like: // "border:solid black 1px" // // The last 5 arguments are only used for borders. Only the first two // arguments are mandatory. // // The multiple components of the "font" attribute are not parsed here // as they are for borders. if (! css_attr) css_attr = dom_prop; debug("getElementStyle(): checking element style, dom_prop="+dom_prop+", css_attr="+css_attr,e); var sty; if (e.style) { if ((sty = e.style[dom_prop])) { debug("getElementStyle: e.style["+dom_prop+"]="+sty,e); } else if (dom_gen_prop != null && (sty = e.style[dom_gen_prop])) { debug("getElementStyle: e.style["+dom_gen_prop+"],"+position+"="+sty,e); sty = parseStyleSetting(sty,position); } else if (dom_border_prop != null && (sty = e.style[dom_border_prop])) { debug("getElementStyle: e.style["+dom_border_prop+"]="+sty,e); sty = parseBorderSetting(sty,component); } else if (dom_border_gen_prop != null && (sty = e.style[dom_border_gen_prop])) { debug("getElementStyle: e.style["+dom_border_gen_prop+"]="+sty,e); sty = parseBorderSetting(sty,component); } if (sty) { if (component == null || component != "width") { return sty; } else { // IE5Mac returns "medium" for unset border widths. // Return style attribute setting only if it is a number, // otherwise look in style sheet settings below. if (sty.match(/\d/)) return sty; } } } debug("getElementStyle(): checking document.defaultView"); var vs, v = document.defaultView; if (v && v.getComputedStyle && (vs = v.getComputedStyle(e,""))) { // W3C DOM (Netscape) sty = vs.getPropertyValue(css_attr); if (sty != null) { debug("getElementStyle: getComputedStyle("+css_attr+")="+sty,e); // Note: Firefox 1.0 (and others?) returns incorrect // margin and padding widths. if (sty == '0px' && e.nodeName == 'TABLE' && (css_gen_attr == 'padding' || css_gen_attr == 'margin')) { ; // fall through and look in style sheet. debug("getElementStyle: falling through to check stylesheet",e); } else { return sty; } } if (css_gen_attr != null) { sty = parseStyleSetting(vs.getPropertyValue(css_gen_attr),position); if (sty != null) { debug("getElementStyle: getComputedStyle("+css_gen_attr+ ","+position+")="+sty,e); return sty; } } if (css_border_attr != null) { sty = parseBorderSetting( vs.getPropertyValue(css_border_attr),component); if (sty != null) { debug("getElementStyle: getComputedStyle("+ css_border_attr+")="+sty,e); return sty; } } if (css_border_gen_attr != null) { sty = parseBorderSetting( vs.getPropertyValue(css_border_gen_attr),component); if (sty != null) { debug("getElementStyle: getComputedStyle("+ css_border_gen_attr+")="+sty,e); return sty; } } } debug("getElementStyle(): checking e.currentStyle"); if (e.currentStyle) { // IE5 not IE4 sty = e.currentStyle[dom_prop]; if (sty != null) { debug("getElementStyle: e.currentStyle["+dom_prop+"]="+sty,e); return sty; } if (dom_gen_prop != null) { sty = parseStyleSetting(e.currentStyle[dom_gen_prop],position); if (sty != null) { debug("getElementStyle: e.currentStyle["+dom_gen_prop+ "],"+position+"="+sty,e); return sty; } } if (dom_border_prop != null) { sty = parseBorderSetting(e.currentStyle[dom_border_prop],component); if (sty != null) { debug("getElementStyle: e.currentStyle["+dom_border_prop+ "]="+sty,e); return sty; } } if (dom_border_gen_prop != null) { sty = parseBorderSetting(e.currentStyle[dom_border_gen_prop],component); if (sty != null) { debug("getElementStyle: e.currentStyle["+dom_border_gen_prop+ "]="+sty,e); return sty; } } } debug("getElementStyle: checking stylesheets rules"); // Brute force. Check all the rules in all the stylesheets. // This is from Apple Computer, plucked from O'Reilly Network. var i = e.id; var re = e.className ? new RegExp("\\."+e.className+"$") : null; var p = dom_prop; // Try styleSheets var sheets = document.styleSheets; if(sheets && sheets.length > 0) { // loop over each sheet var x; for(x = 0; x < sheets.length; x++) { // grab stylesheet rules var rules = sheets[x].cssRules; if(rules && rules.length > 0) { // check each rule var y,t; for(y = 0; y < rules.length; y++) { var z = rules[y].style; if (!z) { debug("getElementStyle(): this stylesheet does not have a rule for "+p,e); continue; } // use the native selectorText and style stuff t = rules[y].selectorText; if (bv.isSafari) { // Remove attribute selector strings that Safari // automatically adds to selectorText. if (t.substr(0,1) == '*') { // Safari reports id selectors #myid as *[ID"myid"] t = '#' + t.substr(5,(t.length-7)); } else { // Safari reports class selectors DIV.myclass[CLASS~="myclass"] // The following weird syntax is to pass IE5 on Mac. t = t.replace((new RegExp("\\[.*?\\]")),""); } } // Uncomment the following to look at all selectors. //debug("getElementStyle(): looking at selectorText=" + // rules[y].selectorText+", ("+t+")"); if(z[p] != '' && z[p] != null && ((t == e.nodeName || t.match(re)) || (e.id && t == '#' + e.id))) { debug("getElementStyle(): found stylesheet rule: " + "document.styleSheets["+x+"].cssRules["+y+"].style["+p+"]="+z[p],e); sty = z[p]; } } } } } if (sty != null) { debug("getElementStyle(): returning stylesheet style: "+sty,e); return sty; } debug("getElementStyle: Cannot find style for "+dom_prop+"("+css_attr+")",e); return null; } function parseStyleSetting(setting,position) { // Parse a style setting for top, bottom, left, or right measurement settings. if (! setting) return null; if (! position) return setting; // Match one, two, three, or four space separated settings. setting.match(/(\S+)(\s+(\S+)(\s+(\S+)(\s+(\S+))?)?)?/); if (position == "top") { return RegExp.$1; } if (position == "right") { if (RegExp.$3) return RegExp.$3; return RegExp.$1; } if (position == "bottom") { if (RegExp.$5) return RegExp.$5; return RegExp.$1; } if (position == "left") { if (RegExp.$7) return RegExp.$7; if (RegExp.$3) return RegExp.$3; return RegExp.$1; } } function parseBorderSetting(setting,component) { // Parse number of pixels from a border setting like: "solid black 1px". // The component argument can be "style", "color", or "width". // Only "width" is parsed here for the menus. if (! component || component == "width") { if (setting.match(/(\d+px)/)) return RegExp.$1; return 0; } } function getBorderWidth(e,position) { // Get element's border width. // Optional position argument can be "top", "bottom", "left", or "right". // This routine always returns a number. A static flag getBorderWidth.found // is set to true if style setting was found. debug("getBorderWidth() begin: position="+position,e); var width; var dom_gen_prop = "borderWidth"; var css_gen_attr = "border-width"; var dom_border_gen_prop = "border"; var css_border_gen_attr = "border"; if (! position) { width = getElementStyle(e, dom_gen_prop, css_gen_attr, null, null, null, dom_border_gen_prop, css_border_gen_attr, "width"); } else { // Capitalize position to use to assemble DOM property names. var Position = position.substr(0,1).toUpperCase()+position.substr(1); var dom_prop = "border" + Position + "Width"; //borderLeftWidth var css_attr = "border-" + position + "-width"; //border-left-width var dom_border_prop = "border" + Position; //borderLeft var css_border_attr = "border-" + position; //border-left width = getElementStyle(e, dom_prop, css_attr, dom_gen_prop, css_gen_attr, position, dom_border_prop, css_border_attr, "width", dom_border_gen_prop, css_border_gen_attr); } // Ignore keywords "thin", "medium", and "thick". if (width == null || ! width.match(/\d/)) { getBorderWidth.found = false; debug("getBorderWidth(): position="+position+", border width="+ (width?width:"''")+" NOT FOUND",e); return 0; } else { getBorderWidth.found = true; debug("getBorderWidth(): position="+position+", width="+width,e); // Parse number of pixels. Assume units are "px". return parseNum(width); } } function getPadding(e,position) { // Get element's padding dimension. // Optional position argument can be "top", "bottom", "left", or "right". // This routine always returns a number. A static flag getPadding.found // is set to true if style setting was found. var p,s = "padding"; if (! position) { p = getElementStyle(e,s,s); } else { // Capitalize position to use to assemble DOM property names. var Position = position.substr(0,1).toUpperCase()+position.substr(1); p = getElementStyle(e, (s+Position), (s+"-"+position), s, s, position); } if (p == null) { getPadding.found = false; debug("getPadding(): position="+position+", padding NOT FOUND",e); return 0; } else { getPadding.found = true; debug("getPadding(): position="+position+", padding="+p,e); // Parse number of pixels. Assume units are "px". return parseNum(p); } } function getMargin(e,position) { // Get element's margin dimension. // Optional position argument can be "top", "bottom", "left", or "right". // This routine always returns a number. A static flag getMargin.found // is set to true if style setting was found. var m,s = "margin"; if (! position) { m = getElementStyle(e,s,s); } else { // Capitalize position to use to assemble DOM property names. var Position = position.substr(0,1).toUpperCase()+position.substr(1); m = getElementStyle(e, (s+Position), (s+"-"+position), s, s, position); } if (m == null) { getMargin.found = false; debug("getMargin(): position="+position+", margin NOT FOUND",e); return 0; } else { getMargin.found = true; debug("getMargin(): position="+position+", margin="+m,e); // Parse number of pixels. Assume units are "px". return parseNum(m); } } /* Following based on "Dynamic HTML The Definitive Reference" 2nd. Ed. by Danny Goodman p92. */ function getElementWidth(e) { // Width including padding and borders, not margins. var result = 0; if (e.offsetWidth) { // (bv.isIE && bv.isIE5Mac) result = e.offsetWidth; debug("getElementWidth(): offsetWidth="+result,e); } else if (e.clip && e.clip.width) { // (bv.isNS && bv.isMoz) result = e.clip.width; debug("getElementWidth(): clip.width="+result,e); } else if (e.style && e.style.pixelWidth) {// bv.isOpr result = e.style.pixelWidth; debug("getElementWidth(): pixelWidth="+result,e); } return parseInt(result); } function getElementHeight(e) { // Height including padding and borders, not margins. var result = 0; if (e.offsetHeight) { // Note: in Netscape && Opera, if element e is an tag // enclosing an , and the CSS style for the tag // display attribute has not been set to "block", the height // returned is the tag's text box height, not the // tag's height. result = e.offsetHeight; debug("getElementHeight(): offsetHeight="+result,e); } else if (e.clip && e.clip.height) { result = e.clip.height; debug("getElementHeight(): clip.height="+result,e); } else if (e.style && e.style.pixelHeight) { result = e.style.pixelHeight; debug("getElementHeight(): pixelHeight="+result,e); } return parseInt(result); } /* * Utility functions */ function parseNum(s) { // Always return a parsed integer. No NaN! if (s) return parseInt("0"+s,10); return 0; } function getElementRef(id) { // Return a reference to the document element. // The id can be a string or an object. if (! id) return null; if (typeof id == "string") { if (document.getElementById) return document.getElementById(id); if (document.all) return document.all[id]; return null; } if (typeof id == "object") return id; return null; } /* * Debug stub */ if (! window.debug) debug = new Function('');