/*
* 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('');