  /**
   *  BPLib.js: Bear Peak Software Inc. Library for JavaScript programming 
   *  in a browser environment.
   *
   *  This library is a miscellaneous collection of functions that the author 
   *  finds useful. The functionality overlaps in some ways with several other 
   *  JavaScript libraries, but it is not intended to duplicate them.
   *  Programmers looking for more complete libraries should check out
   *  Prototype, Scriptaculus, Dojo, YUI, or the GWT.
   */
  
  /* IE flags */

  var isIE = Boolean(window.attachEvent && !window.opera);
  var IEVersion = isIE ? (parseFloat(navigator.appVersion.split("MSIE")[1])) : NaN;
  var IEfixPNG = isIE && IEVersion >= 5.5 && IEVersion < 7.0;
  
  /* event functions */
  
  function registerEvent(element, event, fun, capture) {
    if (element.addEventListener) {
      element.addEventListener(event, fun, (capture || false));
    } else if (element.attachEvent) {
      element.attachEvent("on" + event, fun);
    } else {
      // note: this replaces the one event listener, it does not add
      element["on" + event] = fun;
    }
  }
  
  function unregisterEvent(element, event, fun, capture) {
    var onevent = "on" + event;
    if (element.removeEventListener) {
      element.removeEventListener(event, fun, (capture || false));
    } else if (element.detachEvent) {
      element.detachEvent("on" + event, fun);
    } else {
      // note: this removes the one event listener
      element["on" + event] = null;
    }
  }

  // stopEvent: stops event propagation AND cancels the browser default action
  function stopEvent(e) {
    if (e.preventDefault) { // if standard DOM event model
      e.preventDefault();
      e.stopPropagation();
    } else {                // assume IE event model
      e.returnValue = false;
      e.cancelBubble = true;
    }
  }
  
  function captureMouseEvents(element) {
    if (element.setCapture)
      element.setCapture();
  }
  
  function releaseMouseEvents(element) {
    if (element.releaseCapture)
      element.releaseCapture();
  }
  
  function eventInfo(e) {
    var target = e.target || e.srcElement;
    var info = "" + e.type + " target=" + target + " id='" + 
        (target.id ? target.id : 'null') + "'";
    if (e.eventPhase) {
      var phase = "";
        switch (e.eventPhase) {
        case Event.CAPTURING_PHASE: 
          phase="capturing currentTarget=" + e.currentTarget;
          break;
        case Event.AT_TARGET: 
          phase="at_target";
          break;
        case Event.BUBBLING_PHASE:
          phase="bubbling currentTarget=" + e.currentTarget;
          break;
      }
      info += " eventPhase=" + phase;
    }
    return info;
  }
  
  /**
   * Returns a boolean indicating whether the target for the given event is
   * the given element or one of its descendants.
   */
  function eventTargetInElement(event, element) {
    var target = event.target || event.srcElement;
    while (target != null) {
      if (target == element) return true;
      target = target.parentNode;
    }
    return false;
  }
  
  /**
   * Returns a boolean indicating whether element1 is 
   * element2 or is one of its descendants.
   */
  function elementInElement(element1, element2) {
    while (element1 != null) {
      if (element1 == element2) return true;
      element1 = element1.parentNode;
    }
    return false;
  }
  
  /* HTML Element functions */
  
  function setElementPosition(e, x, y) {
    e.style.left = x + "px";
    e.style.top  = y + "px";
  }
   
  function setElementSize(e, w, h) {
    e.style.width  = w + "px";
    e.style.height = h + "px";
  }
  
  function setElementBounds(e, x, y, w, h) {
    setElementPosition(e, x, y);
    setElementSize(e, w, h);
  }

  function moveElement(e, dx, dy) {
    e.style.left = parseInt(e.style.left) + dx + "px";
    e.style.top  = parseInt(e.style.top)  + dy + "px";
  }

  function getAbsoluteOffsetLeft(e) {
    var left = 0;
    while (e != null) {
      left += e.offsetLeft;
      e = e.offsetParent;
    }
    return left;
  }

  function getAbsoluteOffsetTop(e) {
    var top = 0;
    while (e != null) {
      top += e.offsetTop;
      e = e.offsetParent;
    }
    return top;
  }

  function setOpacity(element, value) {
    // always set opacity (even if ignored in IE)
    element.style.opacity = value;
    
    // set filter with opacity only if IE
    // Note: "alpha" is the IE 4 name, which works with later versions,
    // the IE 5+ longname for alpha is not required
    if (isIE)
      element.style.filter = "alpha(opacity=" + Math.round(value * 100) + ")";
  }
  
  /* CSS */
  
  function getCompStyle(e) {
    var style = null;
    if (e.currentStyle)
      style = e.currentStyle;
    else if (window.getComputedStyle)
      style = window.getComputedStyle(e, null);
    else if (document.defaultView && document.defaultView.getComputedStyle)
      style = document.defaultView.getComputedStyle(e, null);
    return style;
  }
  
  function setStyleFloat(e, value) {
    if (e.style.styleFloat === undefined) {
      e.style.cssFloat = value;
    } else {
      e.style.styleFloat = value;
    }
  }
  
  /* simple logging */
  
  /**
   * Logs the given string to the console if it exists or to HTML element with 
   * id "logElement" if it exists, or does nothing if neither exist.
   */
  function log(s) {
    if (window.console && window.console.log) {
      console.log(s);
    } else {
      var logElement = document.getElementById("logElement");
      if (logElement != null) {
        logElement.appendChild(document.createTextNode(s));
        logElement.appendChild(document.createElement("br"))
      }
    }
  }
  
  /** 
   * Clears the content of the HTML element with id "logElement" if it exists.
   */
  function clearLog() {
    var logElement = document.getElementById("logElement");
    if (logElement != null)
      logElement.innerHTML = "";
  }  


