function DropdownMenu (bases,dropdown_background,tout,click_action) {
  /**
    Author: Pawel Müller
    Date:   2008-12-20
    -------------- Important Note ---------------------------------------------------
    -- When you are instanciating this Class it should be done
    -- AFTER all bases and dropdowns are loaded in the DOM.
    -- You can achieve that e.g. by the following:
    --   <html><head>
    --     <script type="text/javascript">
    --       document.observe('dom:loaded',function () {
    --         dropdown_background = $('dropdown-background');
    --         var myMenu = new DropdownMenu(bases,dropdown_background);
    --       })
    --     </script>
    ---------------------------------------------------------------------------------

    This is a Constructor for a Dropdown Menu, say, you hover an element
    on the screen, and just below it appears a box with subitems to the hovered
    item. I call this box a dropdown.
    What Parameters does this constructor takes?
     - bases:
         An Array of bases which are the items you want to have a dropdown to appear
         when you hover it.
         For now it's an array of ids, not objects!
         From this bases we will generate the observers for the items and a list of
         all available dropdowns in the document. Available means, that for each item in
         the bases array there exists a DOM object with the id <base_item> concatenated with
         the string '_dropdown'.
         Thats our naming convention here:
           if there is a base item with the id <someID>, then the related dropdown has to have
           the id '<someID>_dropdown'.
         A base Item does not obligatorily need to have a dropdown.
     - dropdown_background:
         Additionally you can add another element as the background of the dropdown.
         The initially idea was, that you can set a transparency to that background box via CSS
         (e.g.: "opacity: 0.8;" for not IE and "filter:Alpha(opacity:80;").
         As for know you must deliver a dropdown_background parameter and there must be a
         DOM object with that ID.
         You can deliver this parameter as an object or as an id string.
     - tout:
         Is the time that has to pass, bevore the dropdown disapears on a mouse_out event.

     - click_action:
         this variable has to be a reference to a function which will be called at the onclick command.
         If none is passed, we implement a default click method.

    How does the DropdownMenu work?
      In Short:
        First we set some observers for all the included items. That is, for each item there
        is a mouseover and a mouseout eventlistener set.

  **/

  // alert(this.id)

  this.dropdown_background = (dropdown_background && Object.isString(dropdown_background)) ? $(dropdown_background) : dropdown_background;
  // this.dropdown_background = dropdown_background
  this.bases = bases
  // alert(this.id)
  this.base = null
  this.last_base = null
  this.dropdown = null
  this.dropup = null
  this.dropdown_timeout = null
  this.dropdown_background_timeout = null
  this.base_class_change_timeout = null // this var holds the timeout of the baes class change
  this.dropdowns = new Array() // list of dropdowns for the menu will be automaticaly generated out of the bases Array
  this.coords = null
  this.tout = tout?tout:0.4;
  this.bg_coords_correction_left = 0   // for tweaking the x positioning of the background image
  this.bg_coords_correction_top = 0  // for tweaking the y positioning of the background image
  this.dd_coords_correction_left = 0   // for tweaking the x positioning of the dropdown
  this.dd_coords_correction_top = 0  // for tweaking the y positioning of the dropdown
  this.dropdown_alignment = 'left' // aligns dropdown to left site of dropdown base
                                    // other are "right" or '' if you want to override the this.positioning method
  // this.base_background = ''
  this.base_width = 0
  this.block_dropdowns = false  // if set to true, the dropdown won't drop down
  this.fixed_width = false // if fixed width is true then we set the width of the dropdown. By default this will be the bases width
  this.dropdings = new Array('_','')  // this is needed, as firefox has some problems with events timed very closely. In This variable
                                // we will store, which function was called, so that it won't be executed twice
  this.browser = '' // some stuff in this CLass would like to know which browser i being used. As I implemented
                    // a Browser detection elsewhere, it would be stupid to repeat that step here, so
                    // this attribute gives you the chance to tell the DropdownMenu Object which Browser is being used.
                    // DEFAULT: if a browserobject is defined in the variable browser we'll use that. This is set in the
                    // setBrowser() method during initialization
  this.base_active_class_name = '' // if this is set (from outside) this class name will be asigned to the base when it is active
  // this.log = new logging('logging')
  // this.log.max_log_rows = 40
  this.name = 'DropdownMenu' // just a name for debugging
  this.resized = false; // there's a listener that set's this to true on resize
  // alert('this: '+this.constructor.name)
  // methode mouseOver
  this.mouseOver = function(e) {
    // console.log('this: '+this.constructor.name)
    var elem = e.element();
    e.stop();
    if ( elem.tagName != 'A' ) {
      var elem = Event.findElement(e,'A');
    };
    // resetting the timeout on mouseout of the dropdown
    if ( this.dropdown_timeout ) {
      // debugMessage('dropdown_timeout')
      window.clearTimeout(this.dropdown_timeout);
    };
    if ( this.dropdown_background_timeout ) {
      // debugMessage('dropdown_background_timeout')
      window.clearTimeout(this.dropdown_background_timeout);
    };
    if ( this.base_class_change_timeout ) {
      window.clearTimeout(this.base_class_change_timeout);
    };
    this.base = elem;
    // console.log('cc: '+this.base.id)

    this.dropdown = $(this.base.id+'_dropdown');
    if ( this.base == this.last_base ) {
      // debugMessage('this.base: '+this.base.id)
      // console.log('this.base == this.last_base')
      this.setBaseActiveClass()
      this.dropDown();
    };
    if ( this.base && !this.last_base ) {
      // debugMessage('this.base2: '+this.base.id)
      // console.log('this.base && !this.last_base')
      this.setBaseActiveClass()
      this.dropDown();
    };
    if ( this.base && this.last_base && this.last_base != this.base ) {
      // debugMessage('this.dropup: '+this.dropup.id)
      // console.log('this.base && this.last_base && this.last_base != this.base')
      this.dropdings.shift()  // that and the following line has to be present.
      this.dropdings.push('')
      this.dropUp();
      this.unsetBaseActiveClass()
      // debugMessage('this.base3: '+this.base.id)
      this.setBaseActiveClass()
      this.dropDown();
    };
    this.last_base = this.base;
    this.dropup = $(this.last_base.id+'_dropdown');
    // debugMessage('this.dropup2: '+this.dropup.id)
  };

  // methood mouseOut
  this.mouseOut = function(e) {
    e.stop();
    if ( this.dropdown ) {
      // console.log('going off '+this.base.id)
      this.dropdown_timeout = this.dropdown.hide.bind(this.dropdown).delay(this.tout);
      // alert('timeout? '+this.dropdown_timeout)
      this.dropdown_background_timeout = this.dropdown_background.hide.bind(this.dropdown_background).delay(this.tout);

    };
    // console.log('this.base_class_change_timeout is going to be set')
    this.base_class_change_timeout = this.unsetBaseActiveClass.bind(this).delay(this.tout)
    // console.log('this.base_class_change_timeout: '+this.base_class_change_timeout)
  };

  // method dropDown
  this.dropDown = function(e) {
    // console.log('START dropDown: '+this.dropdown.id);
    this.dropdings.push('dropdown');
    this.dropdings.shift();
    // console.log('dropdings: '+this.dropdings);
    if ( e ) {
      // alert('got an event on this.dropDown')
      // console.log('+++ dropdown elem: '+e.element().tagName)
      if ( this.dropdown_timeout ) {
        // console.log('clearing dropdown_timeout')
        // alert('clearing dropdown_timeout')
        window.clearTimeout(this.dropdown_timeout);
      };
      if ( this.dropdown_background_timeout ) {
        // alert('clearing dropdown_background_timeout')
        // console.log('clearing dropdown_background_timeout')
        window.clearTimeout(this.dropdown_background_timeout);
      };
      if ( this.base_class_change_timeout ) {
        window.clearTimeout(this.base_class_change_timeout)
      };
    }
    else {
      // console.log('got NO event on this.dropDown')
    }
    if ( !this.dropdown ) {
      this.dropdown_background.hide();
      return;
    };

    if ( ! this.block_dropdowns ) {
      // if ( this.dropdown_cache == this.dropdown ) {
      //   /**
      //     This way we skip the positioning logic, when the dropdown in the cache and the current dropdown are the same.
      //     But once it is positioned, there's no need to do it again. (Pawel at 2011-07-14)
      //   **/
      //   return;
      // };
      // this.dropdown_cache = this.dropdown; // setting the cached dropdown
      // we have to display the dropdown first, otherwise the border widths won't be counted into
      // the width of the hole dropdown box.
      // But we don't want to make it visible, so we hide the box
      this.dropdown.style.visibility = 'hidden';
      // now we display the hidden box
      this.dropdown.show();
      this.positioning();
      this.dropdown.style.visibility = 'visible';
      this.dropdown_background.show();
    };

  };

  this.positioning = function() {
    // console.log('We do positioning.');
    if ( this.dropdown_alignment == 'right' ) {
      this.rightPositioning()
    }
    else if ( this.dropdown_alignment == 'left' ) {
      this.leftPositioning()
    }
  };
  
  /**
    we need this method for fleXcroll, search #flexEmpty
  **/
  this.emptyMethod = function() {};
  
  /**
    removes most of the stuff fleXcroll inserted
  **/
  this.resetFlexCroll = function() {
    // console.log("resetting flexcroll");
    /* copy content of this.dropdown.id+'_contentwrapper' (that's the LI-Elements of the UL Tag) */
    var lis = $(this.dropdown.id+'_contentwrapper').innerHTML;
    /* remove all element in the ul (this.dropdown) and insert the copied LIs into the UL Tag*/
    this.dropdown.update(lis);
    this.dropdown.scrollUpdate = this.emptyMethod; // we set the scrollUpdate method to an empty function (#flexEmpty).
                                                   // This is needed, as fleXcroll registers its own on resize handles
                                                   // which call that method without checking for existence.
    this.dropdown.fleXcroll = null;
    this.dropdown.fleXdata = null;
    this.dropdown.removeClassName('flexcroll');
  };
  
  /**
    arguments:
      top       top left corner coordinate of a dropdown
  **/
  this.setMaxHeightAndDisplayScrollbarsIfNeeded = function(top) {
    // console.log('+++this.dropdown.scrollUpdate: '+(this.dropdown.scrollUpdate?this.dropdown.scrollUpdate:'none'));
    // var display_height = document.viewport.getHeight();  // usable height of the webpage
    var display_height = document.body.clientHeight;  // usable height of the webpage (don't use document.viewport as this won't work in IE quirksmode)
    // this.dropdown.setStyle({height:'auto'}); // without flexcroll this would be sufficient (#noflexcroll)
    var height = this.dropdown.getHeight();
    var bottom_margin = 10; // margin at the top, so that there is a nice gap between dropdown and the bottom of the page
    var max_height = display_height - top; // dropdown height should not be more than that. We do not count in the bottom_margin, so that there are no scrollbars when the dropdown fits exactly into the display
    // console.log("data:\ntop:\t\t"+top+"\nmax_height:\t"+max_height);
    if ( height > max_height ) {
      // console.log("setting height to a fix value of "+(max_height - bottom_margin)+'px');
      this.dropdown.setStyle({height:max_height - bottom_margin +'px'});
      // console.log('setting overflow to auto ');
      this.dropdown.setStyle({'overflow':'auto'});
      this.dropdown.addClassName('flexcroll');
      if ( fleXenv ) {
        // console.log('we have flexcroll.');
        if ( !this.dropdown.scrollUpdate || this.dropdown.scrollUpdate == this.emptyMethod ) {
          // console.log("initializing flexcroll on dropdown "+this.dropdown.id);
          this.dropdown.scrollUpdate = null; // we have to reset the emptyMethod here before reinitialisation
                                             // otherwhise the inistialization won't be complete. (#flexEmpty)
          fleXenv.fleXcrollMain(this.dropdown);
          this.dropdown.scrollUpdate.delay(0.05);
        };
      }
    }
    else {
      // What happens is important, when a user opens a dropdown, which geht's scrollbars with flexcroll.
      // If he resizes the browser window, so that the dropdown would fit without scrollbars, we have to
      // do the following, so that the height get resetted.
      // This is not needed if we won't use flexcroll (search for #noflexcroll).
      // console.log('###########################\n'+(this.dropdown.scrollUpdate != this.emptyMethod)+'\n'+this.dropdown.scrollUpdate+'\n ******** \n'+this.emptyMethod+'\n###########################')
      // console.log('+ this.dropdown.scrollUpdate: '+this.dropdown.scrollUpdate);
      if ( this.dropdown.scrollUpdate && (this.dropdown.scrollUpdate != this.emptyMethod) && this.resized && true ) {
        this.resized = false; // resetting the resized indicator
        this.resetFlexCroll();
        // console.log("setting height to auto");
        this.dropdown.setStyle({height:'auto'});
        this.setMaxHeightAndDisplayScrollbarsIfNeeded(top);
      };
    };
  };
  
  this.rightPositioning = function() {
    this.coords = this.base.cumulativeOffset();
    this.setWidth();
    var base_height = this.base.getHeight();
    var base_width = this.base.getWidth();
    var dropdown_width = this.dropdown.getWidth();
    // to align it on the right base side we need the difference between base_width and dropdown_width
    var diff = base_width - dropdown_width
    this.dropdown.style.top = this.coords.top + base_height + this.dd_coords_correction_top +'px';
    this.dropdown.style.left = this.coords.left + diff + this.dd_coords_correction_left +'px';
    this.dropdown_background.style.top = this.coords.top + base_height + this.bg_coords_correction_top +'px';
    this.dropdown_background.style.left = this.coords.left + diff + this.bg_coords_correction_left +'px';
    this.dropdown_background.style.width = this.dropdown.getWidth()+'px';
    this.dropdown_background.style.height = this.dropdown.getHeight()+'px';
  };

  this.leftPositioning = function() {
    this.coords = this.base.cumulativeOffset();
    this.setWidth()
    // console.log('base left: '+this.coords.left)
    var base_height = this.base.getHeight();
    this.setMaxHeightAndDisplayScrollbarsIfNeeded(this.coords.top + base_height);
    this.dropdown.style.top = this.coords.top + base_height + this.dd_coords_correction_top +'px';
    this.dropdown.style.left = this.coords.left+this.dd_coords_correction_left +'px';
    
    this.dropdown_background.style.top = this.coords.top + base_height + this.bg_coords_correction_top +'px';
    this.dropdown_background.style.left = this.coords.left+this.bg_coords_correction_left +'px';
    // We'll show the background right after it's positioned, and setting the dimensions afterwards
    this.dropdown_background.style.width = this.dropdown.getWidth()+'px';
    this.dropdown_background.style.height = this.dropdown.getHeight()+'px';
    // this.dropdown_background.clonePosition(dropdown);
  };

  this.setBaseActiveClass = function() {
    // console.log('START setBaseActiveClass()')
    if ( this.base != null && this.base_active_class_name != '' ) {
      this.base.addClassName(this.base_active_class_name)
    };
    // console.log('END  setBaseActiveClass()')
  };

  this.unsetBaseActiveClass = function() {
    // console.log("START unsetBaseActiveClass()")
    if ( this.last_base != null && this.base_active_class_name != '' ) {
      this.last_base.removeClassName(this.base_active_class_name)
    };
    // console.log('END  unsetBaseActiveClass()')
  };

  // method dropUp
  this.dropUp = function(e) {
    // console.log('START dropUp')
    // console.log('START dropUp: '+this.dropdown.id);
    this.dropdings.push('dropup')
    this.dropdings.shift()

    if ( this.dropdings[0] == this.dropdings[1] ) {
      // console.log('that particular situation:\n'+this.dropdings)
      return
    }
    else {
      // console.log('another situation:\n'+this.dropdings)
    }

    if ( e ) {
      // alert('we got an event on dropUp Method')
      // console.log('we got an event on dropUp Method')
      // if ( this.dropdown_timeout ) {
        // console.log('TIMEOUT IS -- TRUE --')
        // console.log('timeout: '+this.dropdown_timeout)
      // }
      // else {
        // console.log('TIMEOUT IS -- FALSE --')
      // }
      e.stop()

      this.dropup = e.element();
      // console.log('--- 1 dropup elem: '+this.dropup.tagName)
      if ( this.dropup.tagName != 'UL' ) {
        // this.dropup = e.findElement('ul');
        this.dropup = this.getParentUL(this.dropup); // using that instead of e.findElement('ul') fixes problems in IE
        // console.log('--- 2 dropup elem: '+this.dropup.tagName)
      }
      if ( this.dropup.tagName == 'UL' ) {
        // console.log('++++ found UL parent: '+this.dropup.tagName)
        this.dropdown_timeout = this.dropup.hide.bind(this.dropup).delay(this.tout)
        this.dropdown_background_timeout = this.dropdown_background.hide.bind(this.dropdown_background).delay(this.tout)
        // console.log('base_class_change_timeout will be set')
        this.base_class_change_timeout = this.unsetBaseActiveClass.bind(this).delay(this.tout)
      }
      else {
        // console.log('we have "document" as node.')
      }
    }
    else{
      // alert('we got NO event on dropUp Method')
      // console.log('we got NO event on dropUp Method')
      if ( !this.dropup ) {
        // console.log('aha, so maybe we are here?')
        return
      };
      this.dropup.hide()
      // console.log('we are in dropUp() Method')
      this.dropdown_background.hide()
      this.unsetBaseActiveClass()
    };
    // console.log('END  dropUp')
  };
  
  this.getParentUL = function(element) {
    if ( element ) {
      while ( element.tagName != 'UL' || element == document ) {
        if ( element.tagName == 'UL' ) {
          break;
        };
        element = element.up();
      };
      return element;
    };
  };

  this.getBaseWidth = function() {
    // console.log('base width: '+this.base.getWidth())
    return this.base.getWidth();
  };

  this.setWidth = function() {
    // var browser_correction = 0
    var left_border = 0
    var right_border = 0
    // because of the IE box modell we need to substract the borders from the width in every browser not being IE
    // console.log('browser: '+this.browser);
    if ( this.browser != 'ie' && this.browser != '') {
      // debugConsoleLog("We do not use IE")
      left_border = this.dropdown.getStyle('border-left-width').replace(/px/,'')
      // console.log("BorderLeftWidth: "+left_border)
      right_border = this.dropdown.getStyle('border-right-width').replace(/px/,'')
      // console.log("borderRightWidth: "+right_border)
    };
    if ( this.fixed_width ) {
      this.dropdown.style.width = (this.base_width > 0?this.base_width-left_border-right_border:this.getBaseWidth()-left_border-right_border)+'px'
      // console.log('dropdown setted to this width: '+(this.base_width > 0?this.base_width-left_border-right_border:this.getBaseWidth()-left_border-right_border)+'px')
    }
    // when using with flexcroll the else is needed
    else {
      // console.log("width: (browser is "+this.browser+")"+this.dropdown.getWidth());
      if ( this.dropdown.fleXenv ) {
        this.dropdown.setStyle({width:this.dropdown.getWidth()-left_border-right_border+'px'});
      };
      
    };
    
  };

  this.unsetWidth = function() {
    this.dropdown.style.width = 'auto'
  };

  if ( click_action ) {
    // if we got a function referenz as a custom click method we take that, otherwise we implement the default
    this.click = click_action
  }
  else {
    this.click = function(e) {
      this.dropUp()
      this.dropdown_background.hide()
    }
  }

  this.buildDropdownList = function() {
    // alert('this: '+this.constructor.name)
    var dropdown = null
    for (var i = bases.length - 1; i >= 0; i--){
      dropdown = $(bases[i]+'_dropdown')
      if ( dropdown ) {
        this.dropdowns.push(dropdown)
      };
    };
    // debugMessage('bla: '+this.dropdowns)
  };
  
  this.setResize = function() {
    this.resized = true;
    // console.log('+++++ resized');
  };
  
  this.setBrowser = function() {
    try {
      if ( browser ) {
        this.browser = window.browser.getName();
      };
    } catch(e) {};
  };
  
  this.initialize = function() {
    // We build a list of all dropdowns
    this.setBrowser();
    // console.log('**** browser is: '+this.browser);
    this.buildDropdownList()
    Event.observe(window, 'resize', this.setResize.bind(this));
    // We set the eventlisteners here
    // for the bases
    for (var i = this.bases.length - 1; i >= 0; i--){
      // alert($(this.bases[i]).id)
      $(this.bases[i]).observe('mouseover', this.mouseOver.bind(this));
      $(this.bases[i]).observe('mouseout', this.mouseOut.bind(this));
      $(this.bases[i]).observe('click', this.click.bind(this));
    };
    // for the dropdowns
    for (var i = this.dropdowns.length - 1; i >= 0; i--){

      $(this.dropdowns[i]).observe('mouseout',this.dropUp.bind(this))
      $(this.dropdowns[i]).observe('mouseover',this.dropDown.bind(this))
      // alert('this.dropdowns['+i+']: '+this.dropdowns[i].id)
      $(this.dropdowns[i]).observe('click',this.click.bind(this))
    };
    
    
  };

  this.initialize()
}

