text: // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
  //
  // See scriptaculous.js for full license.
  
  
  Object.debug = function(obj) {
    var info = [];
    
    if(typeof obj in ["string","number"]) {
      return obj;
    } else {
      for(property in obj)
        if(typeof obj[property]!="function")
          info.push(property + ' => ' + 
            (typeof obj[property] == "string" ?
              '"' + obj[property] + '"' :
              obj[property]));
    }
    
    return ("'" + obj + "' #" + typeof obj + 
      ": {" + info.join(", ") + "}");
  }
  
  
  String.prototype.toArray = function() {
    var results = [];
    for (var i = 0; i < this.length; i++)
      results.push(this.charAt(i));
    return results;
  }
  
  /*--------------------------------------------------------------------------*/
  
  var Builder = {
    NODEMAP: {
      AREA: 'map',
      CAPTION: 'table',
      COL: 'table',
      COLGROUP: 'table',
      LEGEND: 'fieldset',
      OPTGROUP: 'select',
      OPTION: 'select',
      PARAM: 'object',
      TBODY: 'table',
      TD: 'table',
      TFOOT: 'table',
      TH: 'table',
      THEAD: 'table',
      TR: 'table'
    },
    // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
    //       due to a Firefox bug
    node: function(elementName) {
      elementName = elementName.toUpperCase();
      
      // try innerHTML approach
      var parentTag = this.NODEMAP[elementName] || 'div';
      var parentElement = document.createElement(parentTag);
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
      var element = parentElement.firstChild || null;
        
      // see if browser added wrapping tags
      if(element && (element.tagName != elementName))
        element = element.getElementsByTagName(elementName)[0];
      
      // fallback to createElement approach
      if(!element) element = document.createElement(elementName);
      
      // abort if nothing could be created
      if(!element) return;
  
      // attributes (or text)
      if(arguments[1])
        if(this._isStringOrNumber(arguments[1]) ||
          (arguments[1] instanceof Array)) {
            this._children(element, arguments[1]);
          } else {
            var attrs = this._attributes(arguments[1]);
            if(attrs.length) {
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
              element = parentElement.firstChild || null;
              // workaround firefox 1.0.X bug
              if(!element) {
                element = document.createElement(elementName);
                for(attr in arguments[1]) 
                  element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
              }
              if(element.tagName != elementName)
                element = parentElement.getElementsByTagName(elementName)[0];
              }
          } 
  
      // text, or array of children
      if(arguments[2])
        this._children(element, arguments[2]);
  
       return element;
    },
    _text: function(text) {
       return document.createTextNode(text);
    },
    _attributes: function(attributes) {
      var attrs = [];
      for(attribute in attributes)
        attrs.push((attribute=='className' ? 'class' : attribute) +
            '="' + attributes[attribute].toString().escapeHTML() + '"');
      return attrs.join(" ");
    },
    _children: function(element, children) {
      if(typeof children=='object') { // array can hold nodes and text
        children.flatten().each( function(e) {
          if(typeof e=='object')
            element.appendChild(e)
          else
            if(Builder._isStringOrNumber(e))
              element.appendChild(Builder._text(e));
        });
      } else
        if(Builder._isStringOrNumber(children)) 
           element.appendChild(Builder._text(children));
    },
    _isStringOrNumber: function(param) {
      return(typeof param=='string' || typeof param=='number');
    }
  }
  
  /* ------------- element ext -------------- */
  
  // adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp
  // note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125
  // instead of "auto" values returns null so it's easier to use with || constructs
  
  String.prototype.camelize = function() {
    var oStringList = this.split('-');
    if(oStringList.length == 1)    
      return oStringList[0];
    var ret = this.indexOf("-") == 0 ? 
      oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
    for(var i = 1, len = oStringList.length; i < len; i++){
      var s = oStringList[i];
      ret += s.charAt(0).toUpperCase() + s.substring(1)
    }
    return ret;
  }
  
  Element.getStyle = function(element, style) {
    element = $(element);
    var value = element.style[style.camelize()];
    if(!value)
      if(document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = (css!=null) ? css.getPropertyValue(style) : null;
      } else if(element.currentStyle) {
        value = element.currentStyle[style.camelize()];
      }
    
    // If top, left, bottom, or right values have been queried, return "auto" for consistency resaons 
    // if position is "static", as Opera (and others?) returns the pixel values relative to root element 
    // (or positioning context?)
    if (window.opera && (style == "left" || style == "top" || style == "right" || style == "bottom"))
      if (Element.getStyle(element, "position") == "static") value = "auto";
      
    if(value=='auto') value = null;
    return value;
  }
  
  // converts rgb() and #xxx to #xxxxxx format,
  // returns self (or first argument) if not convertable
  String.prototype.parseColor = function() {
    color = "#";
    if(this.slice(0,4) == "rgb(") {
      var cols = this.slice(4,this.length-1).split(',');
      var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
    } else {
      if(this.slice(0,1) == '#') {
        if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
        if(this.length==7) color = this.toLowerCase();
      }
    }
    return(color.length==7 ? color : (arguments[0] || this));
  }
  
  Element.makePositioned = function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if(pos =='static' || !pos) {
      element._madePositioned = true;
      element.style.position = "relative";
      // Opera returns the offset relative to the positioning context, when an element is position relative 
      // but top and left have not been defined
      if (window.opera){
        element.style.top = 0;
        element.style.left = 0;
      }  
    }
  }
    
  Element.undoPositioned = function(element) {
    element = $(element);
    if(typeof element._madePositioned != "undefined"){
      element._madePositioned = undefined;
      element.style.position = "";
      element.style.top = "";
      element.style.left = "";
      element.style.bottom = "";
      element.style.right = "";	  
    }
  }
  
  Element.makeClipping = function(element) {
    element = $(element);
    if (typeof element._overflow != 'undefined') return;
    element._overflow = element.style.overflow;
    if((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') element.style.overflow = 'hidden';
  }
  
  Element.undoClipping = function(element) {
    element = $(element);
    if (typeof element._overflow == 'undefined') return;
    element.style.overflow = element._overflow;
    element._overflow = undefined;
  }
  
  Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
    var children = $(element).childNodes;
    var text     = "";
    var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
  
    for (var i = 0; i < children.length; i++) {
      if(children[i].nodeType==3) {
        text+=children[i].nodeValue;
      } else {
        if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
          text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
      }
    }
  
    return text;
  }
  
  Element.setContentZoom = function(element, percent) {
    element = $(element);
    element.style.fontSize = (percent/100) + "em";  
    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
  }
  
  Element.getOpacity = function(element){
    var opacity;
    if (opacity = Element.getStyle(element, "opacity"))
      return parseFloat(opacity);
    if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/))
      if(opacity[1]) return parseFloat(opacity[1]) / 100;
    return 1.0;
  }
  
  Element.setOpacity = function(element, value){
    element= $(element);
    var els = element.style;
    if (value == 1){
      els.opacity = '0.999999';
      if(/MSIE/.test(navigator.userAgent))
        els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'');
    } else {
      if(value < 0.00001) value = 0;
      els.opacity = value;
      if(/MSIE/.test(navigator.userAgent))
        els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + 
          "alpha(opacity="+value*100+")";
    }  
  }
  
  Element.getInlineOpacity = function(element){
    element= $(element);
    var op;
    op = element.style.opacity;
    if (typeof op != "undefined" && op != "") return op;
    return "";
  }
  
  Element.setInlineOpacity = function(element, value){
    element= $(element);
    var els = element.style;
    els.opacity = value;
  }
  
  Element.getDimensions = function(element){
    element = $(element);
    // All *Width and *Height properties give 0 on elements with display "none", 
    // so enable the element temporarily
    if (Element.getStyle(element,'display') == "none"){
      var els = element.style;
      var originalVisibility = els.visibility;
      var originalPosition = els.position;
      els.visibility = "hidden";
      els.position = "absolute";
      els.display = "inline";
      var originalWidth = element.clientWidth;
      var originalHeight = element.clientHeight;
      els.display = "none";
      els.position = originalPosition;
      els.visibility = originalVisibility;
      return {width: originalWidth, height: originalHeight};    
    }
    
    return {width: element.offsetWidth, height: element.offsetHeight};
  } 
  
  /*--------------------------------------------------------------------------*/
  
  Position.positionedOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        p = Element.getStyle(element,'position');
        if(p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  }
  
  // Safari returns margins on body which is incorrect if the child is absolutely positioned.
  // for performance reasons, we create a specialized version of Position.cumulativeOffset for
  // KHTML/WebKit only
  
  if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
    Position.cumulativeOffset = function(element) {
      var valueT = 0, valueL = 0;
      do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        
        if (element.offsetParent==document.body) 
          if (Element.getStyle(element,'position')=='absolute') break;
          
        element = element.offsetParent;
      } while (element);
      return [valueL, valueT];
    }
  }
  
  Position.page = function(forElement) {
    var valueT = 0, valueL = 0;
  
    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
  
      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;
        
    } while (element = element.offsetParent);
  
    element = forElement;
    do {
      valueT -= element.scrollTop  || 0;
      valueL -= element.scrollLeft || 0;    
    } while (element = element.parentNode);
  
    return [valueL, valueT];
  }
  
  // elements with display:none don't return an offsetParent, 
  // fall back to  manual calculation
  Position.offsetParent = function(element) {
    if(element.offsetParent) return element.offsetParent;
    if(element == document.body) return element;
    
    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element,'position')!='static')
        return element;
    
    return document.body;
  }
  
  Position.clone = function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})
    
    // find page position of source
    source = $(source);
    var p = Position.page(source);
  
    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements, 
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }
    
    // correct by body offsets (fixes Safari)
    if (parent==document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop; 
    }
  
    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + "px";
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + "px";
    if(options.setWidth)  target.style.width = source.offsetWidth + "px";
    if(options.setHeight) target.style.height = source.offsetHeight + "px";
  }
  
  Position.absolutize = function(element) {
    element = $(element);
    if(element.style.position=='absolute') return;
    Position.prepare();
  
    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;
  
    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;
  
    element.style.position = 'absolute';
    element.style.top    = top + 'px';;
    element.style.left   = left + 'px';;
    element.style.width  = width + 'px';;
    element.style.height = height + 'px';;
  }
  
  Position.relativize = function(element) {
    element = $(element);
    if(element.style.position=='relative') return;
    Position.prepare();
  
    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
  
  /*--------------------------------------------------------------------------*/
  
  Element.Class = {
      // Element.toggleClass(element, className) toggles the class being on/off
      // Element.toggleClass(element, className1, className2) toggles between both classes,
      //   defaulting to className1 if neither exist
      toggle: function(element, className) {
        if(Element.Class.has(element, className)) {
          Element.Class.remove(element, className);
          if(arguments.length == 3) Element.Class.add(element, arguments[2]);
        } else {
          Element.Class.add(element, className);
          if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
        }
      },
  
      // gets space-delimited classnames of an element as an array
      get: function(element) {
        return $(element).className.split(' ');
      },
  
      // functions adapted from original functions by Gavin Kistner
      remove: function(element) {
        element = $(element);
        var removeClasses = arguments;
        $R(1,arguments.length-1).each( function(index) {
          element.className = 
            element.className.split(' ').reject( 
              function(klass) { return (klass == removeClasses[index]) } ).join(' ');
        });
      },
  
      add: function(element) {
        element = $(element);
        for(var i = 1; i < arguments.length; i++) {
          Element.Class.remove(element, arguments[i]);
          element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
        }
      },
  
      // returns true if all given classes exist in said element
      has: function(element) {
        element = $(element);
        if(!element || !element.className) return false;
        var regEx;
        for(var i = 1; i < arguments.length; i++) {
          if((typeof arguments[i] == 'object') && 
            (arguments[i].constructor == Array)) {
            for(var j = 0; j < arguments[i].length; j++) {
              regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
              if(!regEx.test(element.className)) return false;
            }
          } else {
            regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
            if(!regEx.test(element.className)) return false;
          }
        }
        return true;
      },
  
      // expects arrays of strings and/or strings as optional paramters
      // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
      has_any: function(element) {
        element = $(element);
        if(!element || !element.className) return false;
        var regEx;
        for(var i = 1; i < arguments.length; i++) {
          if((typeof arguments[i] == 'object') && 
            (arguments[i].constructor == Array)) {
            for(var j = 0; j < arguments[i].length; j++) {
              regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
              if(regEx.test(element.className)) return true;
            }
          } else {
            regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
            if(regEx.test(element.className)) return true;
          }
        }
        return false;
      },
  
      childrenWith: function(element, className) {
        var children = $(element).getElementsByTagName('*');
        var elements = new Array();
  
        for (var i = 0; i < children.length; i++)
          if (Element.Class.has(children[i], className))
            elements.push(children[i]);
  
        return elements;
      }
  }