//by dezinerfolio.com

eval((function(){a=" {3document5ByIdOgvv=8E!&&E!K32}o=E;E;v=parseInt(2E=o;3vdheight=vHpx}Oofvgvv=v/d.h;dDv;dfilterKalpha(D+v*100H)Obz>0z#-1:z-v90E;%eE;z<x(x-z)#+1:v+z9x%Accordians,fl=5sByTagName(diva=[]d=S;QQ,Q)=={a.push}}xL((hc=h0,hc6==hcMc+;c;coverflowKvisible;c.h=g(cc.s=(s=8)?7:s;h.f=f;h.c=a;h.match4fHH))x=h}hC=(Rj=0;j<F.c;j++n=F.c[j];n=n0,ndMn6nMn+;s=dJt4sH)Rp=0;p<s;p++s[p]=KGsJce(p,1d=s.join( break}}clearn.tQ==FPg(n,0@ed+K GP@b}}}}})(S)}x!LxC()}}",b=49;while(b>=0)a=a.replace(new RegExp("#%2345689@CDEFGHJKLMOPQRS".charAt(b),"g"),("\\\042\044.style.displayfunctionInterval(.className.length.indexOf(-)(d)if(-content).id){.style.=none{d=;z=g;x=d.h;(d,;for(i=0;i<l;i++);}else{('+n+')',9)}.substr(=blockv=Math.round(/d.sv=(v<1)?zcleard.t)}} d.offsetHeightreturn (new RegExp(.getElement+-header=undefined;ofv)gn.t=set'.onclickopacity=dthis+d.f+.spli==null=(} n=dfor(l[i]".split(""))[b--]);return a})())
/* Added to config.js to avoid the dependency on YUI */
if (typeof BOF == "undefined") {
    var BOF = {};
}
BOF.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=BOF;
        for (j=(d[0] == "BOF") ? 1 : 0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
    return o;
};

BOF.namespace('BOF.EU.Shopping');
BOF.EU.Shopping.config = {
  tracking: {
    bEnabled: true,
    baseUrl: "http://fr.rd.BOF.com/shopping/kelkoo/homepage", // No trailing slash
    uastring: "Googlebot|Slurp|msnbot|Teoma",
    productsearchId : "productsearch",
    searchFormId : 's',
    aModuleIds : ['globalnav','vct','home-promo','hot-products','home-topcat','misc-infos','gen-ter-mod','tagcloud','feat-stores','country-misc','intl-cpy','spec-sites']
  },
  globalnav:{
    dataURL: '/ctl/do/asyncCall/navigation-data?partner=kelkoo&hash=GuCajuJgVbWMvUx3KqDqvH0QCkE-&aggressiveCaching=true'
  },
  caroEditorial: { 
    vContainer: 'ed-caro',
    sLoopMode: 'continuous',
    bRandomStart: true,
    bAutoPlay: true,
    iAutoPlayCount: 3,
    iAutoPlayInterval: 4000,
    oControls: {
      sClassName: 'controls',
      sInsertion: 'after',
      sNextTxt: 'Next',
      sPrevTxt: 'Previous',
      vRef: 'ed-caro'
    },
    oPagination: {
      sClassName: 'pagination',
      sInsertion: 'append',
      vRef: 'home-promo'
    }
  },
  caroPopcats: { 
    vContainer: 'pop-cats',
    sLoopMode: 'continuous',
    iNumSlidesVisible: 3,
    iSlideAdvance: 3,
    bRandomStart: false,
    oControls: {
      sClassName: 'controls',
      sInsertion: 'after',
      sNextTxt: 'Next',
      sPrevTxt: 'Previous',
      vRef: 'pop-cats'
    },
    oPagination: {
      sClassName: 'pagination',
      sInsertion: 'append',
      vRef: 'home-topcat'
    }
  },
  chartsProductOverlay : { delegateId : 'top-products' },
  levelsProductOverlay : { delegateId : 'level-results-page' }
};




var down = false;
function toggleDown() {
        if(down==false){
		down=true;
		t1 = new Tween(document.getElementById('menu_holder').style, 'top', Tween.strongEaseOut, -58, 0, .6, 'px');
		t1.start();
		}
}
function toggleUp() {
        if(down==true){
        down=false;
		t1 = new Tween(document.getElementById('menu_holder').style, 'top', Tween.strongEaseIn, 0, -58, .4, 'px');
		t1.start();
}
}
/**********************************************************************
TERMS OF USE - EASING EQUATIONS
Open source under the BSD License.
Copyright (c) 2001 Robert Penner
JavaScript version copyright (C) 2006 by Philippe Maegerman
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

   * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
   * Neither the name of the author nor the names of contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*****************************************/
function Delegate() {}
Delegate.create = function (o, f) {
	var a = new Array() ;
	var l = arguments.length ;
	for(var i = 2 ; i < l ; i++) a[i - 2] = arguments[i] ;
	return function() {
		var aP = [].concat(arguments, a) ;
		f.apply(o, aP);
	}
}

Tween = function(obj, prop, func, begin, finish, duration, suffixe){
	this.init(obj, prop, func, begin, finish, duration, suffixe)
}
var t = Tween.prototype;

t.obj = new Object();
t.prop='';
t.func = function (t, b, c, d) { return c*t/d + b; };
t.begin = 0;
t.change = 0;
t.prevTime = 0;
t.prevPos = 0;
t.looping = false;
t._duration = 0;
t._time = 0;
t._pos = 0;
t._position = 0;
t._startTime = 0;
t._finish = 0;
t.name = '';
t.suffixe = '';
t._listeners = new Array();	
t.setTime = function(t){
	this.prevTime = this._time;
	if (t > this.getDuration()) {
		if (this.looping) {
			this.rewind (t - this._duration);
			this.update();
			this.broadcastMessage('onMotionLooped',{target:this,type:'onMotionLooped'});
		} else {
			this._time = this._duration;
			this.update();
			this.stop();
			this.broadcastMessage('onMotionFinished',{target:this,type:'onMotionFinished'});
		}
	} else if (t < 0) {
		this.rewind();
		this.update();
	} else {
		this._time = t;
		this.update();
	}
}
t.getTime = function(){
	return this._time;
}
t.setDuration = function(d){
	this._duration = (d == null || d <= 0) ? 100000 : d;
}
t.getDuration = function(){
	return this._duration;
}
t.setPosition = function(p){
	this.prevPos = this._pos;
	var a = this.suffixe != '' ? this.suffixe : '';
	this.obj[this.prop] = Math.round(p) + a;
	this._pos = p;
	this.broadcastMessage('onMotionChanged',{target:this,type:'onMotionChanged'});
}
t.getPosition = function(t){
	if (t == undefined) t = this._time;
	return this.func(t, this.begin, this.change, this._duration);
};
t.setFinish = function(f){
	this.change = f - this.begin;
};
t.geFinish = function(){
	return this.begin + this.change;
};
t.init = function(obj, prop, func, begin, finish, duration, suffixe){
	if (!arguments.length) return;
	this._listeners = new Array();
	this.addListener(this);
	if(suffixe) this.suffixe = suffixe;
	this.obj = obj;
	this.prop = prop;
	this.begin = begin;
	this._pos = begin;
	this.setDuration(duration);
	if (func!=null && func!='') {
		this.func = func;
	}
	this.setFinish(finish);
}
t.start = function(){
	this.rewind();
	this.startEnterFrame();
	this.broadcastMessage('onMotionStarted',{target:this,type:'onMotionStarted'});
	//alert('in');
}
t.rewind = function(t){
	this.stop();
	this._time = (t == undefined) ? 0 : t;
	this.fixTime();
	this.update();
}
t.fforward = function(){
	this._time = this._duration;
	this.fixTime();
	this.update();
}
t.update = function(){
	this.setPosition(this.getPosition(this._time));
	}
t.startEnterFrame = function(){
	this.stopEnterFrame();
	this.isPlaying = true;
	this.onEnterFrame();
}
t.onEnterFrame = function(){
	if(this.isPlaying) {
		this.nextFrame();
		setTimeout(Delegate.create(this, this.onEnterFrame), 0);
	}
}
t.nextFrame = function(){
	this.setTime((this.getTimer() - this._startTime) / 1000);
	}
t.stop = function(){
	this.stopEnterFrame();
	this.broadcastMessage('onMotionStopped',{target:this,type:'onMotionStopped'});
}
t.stopEnterFrame = function(){
	this.isPlaying = false;
}

t.continueTo = function(finish, duration){
	this.begin = this._pos;
	this.setFinish(finish);
	if (this._duration != undefined)
		this.setDuration(duration);
	this.start();
}
t.resume = function(){
	this.fixTime();
	this.startEnterFrame();
	this.broadcastMessage('onMotionResumed',{target:this,type:'onMotionResumed'});
}
t.yoyo = function (){
	this.continueTo(this.begin,this._time);
}

t.addListener = function(o){
	this.removeListener (o);
	return this._listeners.push(o);
}
t.removeListener = function(o){
	var a = this._listeners;	
	var i = a.length;
	while (i--) {
		if (a[i] == o) {
			a.splice (i, 1);
			return true;
		}
	}
	return false;
}
t.broadcastMessage = function(){
	var arr = new Array();
	for(var i = 0; i < arguments.length; i++){
		arr.push(arguments[i])
	}
	var e = arr.shift();
	var a = this._listeners;
	var l = a.length;
	for (var i=0; i<l; i++){
		if(a[i][e])
		a[i][e].apply(a[i], arr);
	}
}
t.fixTime = function(){
	this._startTime = this.getTimer() - this._time * 1000;
}
t.getTimer = function(){
	return new Date().getTime() - this._time;
}
Tween.backEaseIn = function(t,b,c,d,a,p){
	if (s == undefined) var s = 1.70158;
	return c*(t/=d)*t*((s+1)*t - s) + b;
}
Tween.backEaseOut = function(t,b,c,d,a,p){
	if (s == undefined) var s = 1.70158;
	return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
}
Tween.backEaseInOut = function(t,b,c,d,a,p){
	if (s == undefined) var s = 1.70158; 
	if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
	return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}
Tween.elasticEaseIn = function(t,b,c,d,a,p){
		if (t==0) return b;  
		if ((t/=d)==1) return b+c;  
		if (!p) p=d*.3;
		if (!a || a < Math.abs(c)) {
			a=c; var s=p/4;
		}
		else 
			var s = p/(2*Math.PI) * Math.asin (c/a);
		
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	
}
Tween.elasticEaseOut = function (t,b,c,d,a,p){
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
	}
Tween.elasticEaseInOut = function (t,b,c,d,a,p){
	if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) var p=d*(.3*1.5);
	if (!a || a < Math.abs(c)) {var a=c; var s=p/4; }
	else var s = p/(2*Math.PI) * Math.asin (c/a);
	if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
}

Tween.bounceEaseOut = function(t,b,c,d){
	if ((t/=d) < (1/2.75)) {
		return c*(7.5625*t*t) + b;
	} else if (t < (2/2.75)) {
		return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
	} else if (t < (2.5/2.75)) {
		return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
	} else {
		return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
	}
}
Tween.bounceEaseIn = function(t,b,c,d){
	return c - Tween.bounceEaseOut (d-t, 0, c, d) + b;
	}
Tween.bounceEaseInOut = function(t,b,c,d){
	if (t < d/2) return Tween.bounceEaseIn (t*2, 0, c, d) * .5 + b;
	else return Tween.bounceEaseOut (t*2-d, 0, c, d) * .5 + c*.5 + b;
	}

Tween.strongEaseInOut = function(t,b,c,d){
	return c*(t/=d)*t*t*t*t + b;
	}

Tween.regularEaseIn = function(t,b,c,d){
	return c*(t/=d)*t + b;
	}
Tween.regularEaseOut = function(t,b,c,d){
	return -c *(t/=d)*(t-2) + b;
	}

Tween.regularEaseInOut = function(t,b,c,d){
	if ((t/=d/2) < 1) return c/2*t*t + b;
	return -c/2 * ((--t)*(t-2) - 1) + b;
	}
Tween.strongEaseIn = function(t,b,c,d){
	return c*(t/=d)*t*t*t*t + b;
	}
Tween.strongEaseOut = function(t,b,c,d){
	return c*((t=t/d-1)*t*t*t*t + 1) + b;
	}

Tween.strongEaseInOut = function(t,b,c,d){
	if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
	return c/2*((t-=2)*t*t*t*t + 2) + b;
	}
function registerNamespace(ns)
{
    var parts = ns.split(".");
    var root = window;

    for(var i = 0; i < parts.length; i++)
    {
        if(!root[parts[i]])
        {
            root[parts[i]] = new Object();
        }

        root = root[parts[i]];
    }
}

registerNamespace('Associates.Util');

Associates.Util.getElementsByTagAndClass = function(tag, class_name, parent_element)
{
    if(!parent_element){ parent_element = document; }
    
    var tags = parent_element.getElementsByTagName(tag);
   
    var class_tags = new Array();

    var class_regex = new RegExp("\\b" + class_name + "\\b");
    for (var i = 0; i < tags.length; i++)
    {
        if(tags[i].className.match(class_regex))
        {
            class_tags.push(tags[i]);
        }
    }
    
    return class_tags;
}

Associates.Util.findAncestor = function(child, test)
{
    var test_func;

    if(typeof test == 'object')
    {
        test_func = function(obj){ return obj == test };
    }
    else if (typeof test == 'string')
    {
        test_func = function(obj){ return obj.nodeName == test };
    }
    else
    {
        test_func = test;
    }

    while(child && !test_func(child))
    {
        child = child.parentNode;
    }

    return child;
}

Associates.Util.getSelectedRadio = function(form, radioName)
{
    if (typeof form == 'string') { form = document.forms[form]; }

    for (var i = 0; i < form[radioName].length; i++)
    {
        if (form[radioName][i].checked)
        {
            return form[radioName][i].value;
        }
    }
    
    return undefined;
}

      
function AttachEventListener(object, event, handler, useCapture)
{
    if( !useCapture )
    {
        useCapture = false;
    }

    if( object.addEventListener )
    {
        object.addEventListener(event, handler, useCapture);
    }
    else
    {
        object.attachEvent('on' + event, handler);
    }
}


registerNamespace('Assoc.DOMEvent');
Assoc.DOMEvent.AddHandler = function(object, event, handler, context)
{
    var event_handler;
    if(context) 
        event_handler = function(event){ handler.call(context, event) };
    else
        event_handler = handler;

    AttachEventListener(object, event, event_handler);
}

function GetEventTarget(event)
{
    return (event.target)? event.target : event.srcElement;
}

//borrowed from quirksmode. Stops event bubbling.
function StopEventPropagation(event)
{
    if(!event) { event = window.event; }

    //MSIE
    event.cancelBubble = true;

    //w3c
    if(event.stopPropagation) { event.stopPropagation(); }
}

function StopEventDefault(event)
{
    if(!event) { event = window.event; }

    //MSIE
    event.returnValue = false;

    //w3c
    if(event.preventDefault) { event.preventDefault() };
}

//does both StopEventPropogation and StopEventDefault
function StopEvent(event)
{
    StopEventDefault(event);
    StopEventPropagation(event);
}

function mouseOverFromElement(event)
{
    return (event.relatedTarget)? event.relatedTarget : event.fromTarget;
}

function mouseOutToElement(event)
{
    return (event.relatedTarget)? event.relatedTarget : event.toTarget;
}

      /**
 * Copyright (c) 2007 Amazon.com, Inc.
 * All rights reserved.
 */

function AJSEventListener()
{
  this.events = new Object();
}
new AJSEventListener();

AJSEventListener.prototype.registerEvent = function(eventType, handler)
{
  if (!handler || !handler.handleEvent) return;

  if (this.events[eventType] == null) {
    this.events[eventType] = new Object();
    this.events[eventType].handlers = new Array();
  }
  this.events[eventType].handlers.push(handler);

  if(handler.initialize)
  {
      handler.initialize();
  }
}

AJSEventListener.prototype.handleEvent = function(event)
{
  if (event == null || event.type == null) return;

  if (this.events[event.type] != null)
  {
    for (var i = 0; i < this.events[event.type].handlers.length; i++)
    {
      this.events[event.type].handlers[i].handleEvent(event);
    }
  }
}

function AJSEventHandler(init, handle)
{
  this.initialize = init;
  this.handleEvent = handle;
}

function AJSEvent(type)
{
    this.type = type;
}

      function getY(obj)
{
    var y = obj.offsetTop;
    var p = obj.offsetParent;
    while (p)
    {
        y += p.offsetTop;
        p = p.offsetParent;
    }
    return y;
}

function getX(obj)
{
    var x = obj.offsetLeft;
    var p = obj.offsetParent;
    while (p)
    {
        x += p.offsetLeft;
        p = p.offsetParent;
    }
    return x;
}

      
registerNamespace('Assoc');

/* Assoc.PopupDiv
 * provides an easy object API for floating content divs.
 * Also provides events to notify when the div has been opened or closed. */
Assoc.PopupDiv = function(element)
{
    this.content     = element;

    Assoc.DOMEvent.AddHandler(window, 'load', this.onLoad, this);
    
    // I wasn't absolutely positive that this was the right layer to throw the
    // open/close events as opposed to a popup div manager like HotspotPopupDiv
    // but it works for me for now, feel free to rearchitect.
    this.events = new AJSEventListener();
}

Assoc.PopupDiv.prototype.visible = function()
{
    return this.content.style.visibility != 'hidden';
}

/* takes an array specifying position attributes (left, top, right etc...)
 * moves the div to the end of the DOM tree, applies the position attributes,
 * and displays the div */
Assoc.PopupDiv.prototype.show = function(el_pos)
{
    //move the content div to the end of body
    this.content.parentNode.removeChild(this.content);
    document.body.appendChild(this.content);
    
    // funny things happen if you don't clear these out.
    this.content.style.left     = '';
    this.content.style.right    = '';
    this.content.style.top      = '';
    this.content.style.bottom   = '';
    
    //apply the position attributes
    if(el_pos)
    {
        var attrs = ['top', 'bottom', 'left', 'right'];
        for(var i = 0; i < attrs.length; i++)
        {
            if(el_pos[attrs[i]]) 
                this.content.style[attrs[i]] = el_pos[attrs[i]];
        }
    }
    
    //show it
    this.content.style.visibility = 'visible';

    this.events.handleEvent(
            new Assoc.PopupDiv.Event(this, Assoc.PopupDiv.SHOW_EVENT));
}

Assoc.PopupDiv.prototype.hide = function()
{
    this.content.style.visibility = 'hidden';
    this.events.handleEvent(
            new Assoc.PopupDiv.Event(this, Assoc.PopupDiv.HIDE_EVENT));
}

Assoc.PopupDiv.prototype.onLoad = function(event)
{
    // move the content div to the end of body so we don't mess up the display
    // when we flip the display style.
    this.content.parentNode.removeChild(this.content);
    document.body.appendChild(this.content);

    // we use visibility hidden rather than display none because when display
    // is 'none', positioning algorithms can't access the height/width
    // properties of the div.
    this.content.style.position   = 'absolute';
    this.content.style.visibility = 'hidden';
    this.content.style.display    = '';
}

Assoc.PopupDiv.HIDE_EVENT = 'hide';
Assoc.PopupDiv.SHOW_EVENT = 'show';

Assoc.PopupDiv.Event = function(popup, type)
{
    this.base = AJSEvent;
    this.base(type);
    this.popup = popup;
}
Assoc.PopupDiv.Event.prototype = new AJSEvent(LISTBOX_SELECTION_EVENT);

Assoc.PopupDiv.Positioner = function(){};
Assoc.PopupDiv.Positioner.prototype.position = function(
        content_el, rel_el){};

Assoc.PopupDiv.OpenSpacePositioner = function(offset_x, offset_y)
{
    this.offset_x = offset_x;
    this.offset_y = offset_y;
}

Assoc.PopupDiv.OpenSpacePositioner.prototype.position 
    = function(content_el, rel_el)
{
    var parent = document.body;
    
    var screen_middle_x = parent.clientWidth / 2;
    var screen_middle_y = parent.clientHeight / 2;

    var trigger_left    = getX(rel_el);
    var trigger_right   = trigger_left + rel_el.clientWidth;
    var trigger_top     = getY(rel_el);
    var trigger_bottom  = trigger_top + rel_el.clientHeight;
    
    //where's the most space? I prefer go_left && go_down if possible
    var go_left = false;
    go_left = (screen_middle_x >= parent.clientWidth - trigger_right);

    var go_down = false;
    go_down = (screen_middle_y <= parent.clientHeight - trigger_bottom);
    
    var pos = { top : '', bottom : '', left : '', right : ''};
    
    if(go_left)
    {
        var left = trigger_left - this.offset_x - content_el.clientWidth;
        if(left < 0)
            pos.left = '0px';
        else
            pos.left = left + 'px';
    }
    else
    {
        var left = trigger_right + this.offset_x;
        if(left + content_el.clientWidth > parent.clientWidth)
            pos.right = '0px';
        else
            pos.left = left + 'px';
    }
    
    if(go_down)
    {
        var top = trigger_bottom + this.offset_y;
        if(top + content_el.clientHeight > parent.clientHeight)
            pos.bottom = '0px';
        else
            pos.top = top + 'px';
    }
    else
    {
        var top = trigger_top - this.offset_y - content_el.clientHeight;
        if(top < 0)
            pos.top = '0px';
        else
            pos.top = top + 'px';
    }
    
    return pos;
}

      
registerNamespace('Assoc');

Assoc.HotspotPopupDiv = function(content, config)
{
    if(content instanceof Assoc.PopupDiv)
    {
        this.popup = content;
    }
    else
    {
        this.popup   = new Assoc.PopupDiv(content);
    }

    // defaults 
    this.config = {
        trigger_on_click    : true,
        close_on_body_click : true,
        hover_open_delay    : 750,
        hover_close_delay   : 1000,
        positioner          : new Assoc.PopupDiv.OpenSpacePositioner(
                                    5,5)
    };
    
    // overwrite with passed in values
    for(var attr in config)
    {
        this.config[attr] = config[attr];
    }

    //setup content handlers
    if(this.config.hover_open_delay > -1 || this.config.hover_close_delay > -1)
    {
        Assoc.DOMEvent.AddHandler(
                this.popup.content, 'mouseover', this.onContentHover, this);
        Assoc.DOMEvent.AddHandler(
                this.popup.content, 'mouseout', this.onContentUnhover, this);
    }

    if(this.config.close_on_body_click)
    {
        Assoc.DOMEvent.AddHandler(
                window, 'click', this.onBodyClick, this);

        // This is tricky, see comments in onBodyClick to find out why
        Assoc.DOMEvent.AddHandler(

                this.popup.content, 'click', this.onContentClick, this);
    }
}

Assoc.HotspotPopupDiv.prototype.addTrigger = function(trigger_el)
{
    var me = this;
    if(this.config.trigger_on_click || this.config.close_on_body_click)
    {
        Assoc.DOMEvent.AddHandler(
                trigger_el, 'click', this.onTriggerClick, this);
    }

    if(this.config.hover_open_delay > -1 || this.config.hover_close_delay > -1)
    {
        Assoc.DOMEvent.AddHandler(
                trigger_el, 'mouseover', this.onTriggerHover, this);

        Assoc.DOMEvent.AddHandler(
                trigger_el, 'mouseout', this.onTriggerUnhover, this);
    }

    //we need to be able to distinguish this element
    trigger_el.hotspot_popup_trigger = true;
}

Assoc.HotspotPopupDiv.prototype.onTriggerClick = function(event)
{
    StopEventPropagation(event);
    
    // if !trigger_on_click then we're here because close_on_body_click and all
    // we want is the line above.
    if(!this.config.trigger_on_click) return;
    
    StopEventDefault(event);
    
    var trigger_el = Associates.Util.findAncestor(
            GetEventTarget(event), function(obj) {
                return obj.hotspot_popup_trigger == true 
            });
    
    if(this.popup.visible() && this.opening_trigger == trigger_el)
    {
        //close the thing.
        this.popup.hide();

        //clear any open timer.
        window.clearTimeout(this.open_timer);
    }
    else
    {
        //open
        this._positionAndShow(GetEventTarget(event));

        //clear any close timer
        window.clearTimeout(this.close_timer);
    }
}

Assoc.HotspotPopupDiv.prototype.onTriggerHover = function(event)
{
    // clear any close timer
    window.clearTimeout(this.close_timer);
    
    // do this even if it's already open so we reposition if we've switched
    // triggers
    if(this.config.hover_open_delay > -1)
        this._startOpenTimer(GetEventTarget(event));
}

Assoc.HotspotPopupDiv.prototype.onTriggerUnhover = function(event)
{
    //did we really leave the trigger el?
    var reltgt = mouseOutToElement(event);
    var still_here = Associates.Util.findAncestor(
            reltgt, function(obj) {
                return obj.hotspot_popup_trigger == true 
            });

    if(still_here) return;

    window.clearTimeout(this.open_timer);
    
    if(this.config.hover_close_delay > -1)
        this._startCloseTimer();
}

Assoc.HotspotPopupDiv.prototype.onContentHover = function(event)
{
    // just stop any close timeout
    window.clearTimeout(this.close_timer);
}

Assoc.HotspotPopupDiv.prototype.onContentUnhover = function(event)
{
    if(!this.popup.visible) return;

    //did we really leave the content?
    var reltgt = mouseOutToElement(event);
    var still_here = Associates.Util.findAncestor(
            reltgt, this.popup.content);

    if(still_here) return;

    window.clearTimeout(this.open_timer);

    if(this.config.hover_close_delay > -1)
        this._startCloseTimer();
}

Assoc.HotspotPopupDiv.prototype.onContentClick = function(event)
{
    if(!this.popup.visible()) return;
    StopEventPropagation(event);
}

Assoc.HotspotPopupDiv.prototype.onBodyClick = function(event)
{
    // We have click handlers on both the triggers and the content that stop
    // event propagation. So if we get a click here, we know that the click
    // wasn't on any child element of either of them, therefore we can close the
    // element.
    if(!this.popup.visible()) return;
    
    this.popup.hide();
}

Assoc.HotspotPopupDiv.prototype._positionAndShow = function(opening_target)
{
    var trigger_el = Associates.Util.findAncestor(
            opening_target, function(obj) {
                return obj.hotspot_popup_trigger == true 
            });
    
    // opening_trigger is always the last trigger that opened the content
    this.opening_trigger = trigger_el;
    
    var css = this.config.positioner.position(this.popup.content, trigger_el);
 
    this.popup.show(css);
}

Assoc.HotspotPopupDiv.prototype._startOpenTimer = function(opening_target)
{
    var me = this;
    this.open_timer = window.setTimeout(
            function() {
                me._positionAndShow(opening_target) 
                }, 
            this.config.hover_open_delay );
}

Assoc.HotspotPopupDiv.prototype._startCloseTimer = function()
{
    var me = this;
    this.close_timer = window.setTimeout(
            function() {
                me.popup.hide() 
                },
            this.config.hover_close_delay  );
}

      
var LISTBOX_SELECTION_EVENT = 'listbox_selection';

function DynamicListbox(
    idPrefix,
    elementValues,
    selectedHtmls,
    openOnHover,
    selectedIndex)
{
    this.element_values = elementValues; 
    this.id_prefix      = idPrefix;

    //the element that contains the list elements
    var list_element = document.getElementById(idPrefix + '_list');
    
    // the element that holds the selected value and pops the list open and
    // closed
    var selector_element = document.getElementById(idPrefix + '_selector');
    this.div_manager = new Assoc.HotspotPopupDiv(
            list_element,
            { 
                positioner       : new DynamicListbox.Positioner(),
                hover_open_delay : openOnHover ? 500 : -1,
                hover_close_delay : openOnHover ? 1000 : -1
            });
    this.div_manager.addTrigger(selector_element);
    
    //needed by closeListboxes
    selector_element.listbox_object = this;

    // register for the open event. We'll use it as notification to close all
    // other dynamic-listboxes on the page.
    var me = this;
    this.div_manager.popup.events.registerEvent(
            Assoc.PopupDiv.SHOW_EVENT,
            { handleEvent : function(event) { me.closeListboxes(me) } }
            );

    //save the select box content area
    this.selector_element_content = document.getElementById(
        idPrefix + '_selector_content');
    
    //attach attributes and handlers to the elements in the list.
    var elements = Associates.Util.getElementsByTagAndClass(
            'div', 'dynamic_listbox_element', list_element);

    for(var i = 0; i < elements.length; i++)
    {
        //get the element index from its ID
        var match_vars = elements[i].id.match(/_element_(\d+)$/);
        var element_index;
        if(match_vars)
        {
            element_index = match_vars[1]; 
        }
        else
        {
            //this really shouldn't happen
            continue; 
        }

        //let the element itself hold on it's own attributes.
        elements[i].listbox_selected_html = (selectedHtmls[element_index])?
            selectedHtmls[element_index] : "";
        elements[i].listbox_value         = elementValues[element_index]; 

        //some handlers.
        Assoc.DOMEvent.AddHandler(
                elements[i], 'click', this.onElementClick, this);
        Assoc.DOMEvent.AddHandler(
                elements[i], 'click', this.onElementClick, this);
        Assoc.DOMEvent.AddHandler(
                elements[i], 'mouseover', this.onElementMouseOver, this);
        Assoc.DOMEvent.AddHandler(
                elements[i], 'mouseout', this.onElementMouseOut, this);

        //is this the selected element? (do we have a selected element?)
        if(element_index == selectedIndex)
        {
            this._setSelectedElement(elements[i], true);
        }
    }
    
    //Create an event handler
    this.eventDispatcher = new AJSEventListener();
    
    // make sure the div is closed when the user hits the back button.
    Assoc.DOMEvent.AddHandler(window, 'load',
            function(event) { this.div_manager.popup.hide(); },
            this);
}

DynamicListbox.prototype.getSelectedValue = function()
{
    return this.selected_element.listbox_value;
}

DynamicListbox.prototype.setSelectedValue = function(value)
{
    for(var i = 0; i < this.element_values.length; i++)
    {
        if(this.element_values[i] == value)
        {
            var element = document.getElementById(
                    this.id_prefix + '_element_' + i);
            
            this._setSelectedElement(element, true);

            break;
        }
    }
}

DynamicListbox.prototype.addSelectionEventHandler = function(handler)
{
    this.eventDispatcher.registerEvent(LISTBOX_SELECTION_EVENT, handler);
}

DynamicListbox.prototype._setSelectedElement = function(element, dontFireEvent)
{
    var fire_event; //is this a change?
    if(this.selected_element)
    {
        if(this.selected_element != element)
        {
            fire_event = true;
        }
        
        this.selected_element.className = 'dynamic_listbox_element';
    }
    else
    {
        fire_event = true; 
    }

    element.className = 'dynamic_listbox_element element_selected';
    this.selected_element = element;
    
    this.selector_element_content.innerHTML 
            = this.selected_element.listbox_selected_html;

    if(!dontFireEvent && fire_event)
    {
        var selection_event = new DynamicListboxSelectionEvent(this);
        this.eventDispatcher.handleEvent(selection_event);
    }
}

DynamicListbox.prototype.onElementClick = function(event)
{
    StopEventPropagation(event);
    
    //close the list first, since setSelectedElement throws an event, we 
    //don't want the users handler to block the closing.
    this.div_manager.popup.hide();
    this._setSelectedElement(this._getListElementTarget(event));
}

DynamicListbox.prototype._getListElementTarget = function(event)
{
    //gets you the list element you attached the handler to.
    return Associates.Util.findAncestor(
            GetEventTarget(event),
            function(obj) { 
                return obj.className.match(/\bdynamic_listbox_element\b/) 
                }
            );
}

DynamicListbox.prototype.onElementMouseOver = function(event)
{
    var target = this._getListElementTarget(event);
    
    if(target != this.selected_element)
    {
        target.className = 'dynamic_listbox_element element_mouseover';
    }

}

DynamicListbox.prototype.onElementMouseOut = function(event)
{
    var target = this._getListElementTarget(event);

    if(target != this.selected_element)
    {
        target.className = 'dynamic_listbox_element';
    }
}

//close all listboxes, optionally, specify one that shouldn't be closed.
DynamicListbox.prototype.closeListboxes = function(dont_close)
{
    var divs = Associates.Util.getElementsByTagAndClass(
            'div', 'dynamic_listbox_selector');
    for(var i = 0; i < divs.length; i++)
    {
        if(divs[i].listbox_object != dont_close)
        {
            divs[i].listbox_object.div_manager.popup.hide();
        }
    }
}

/* A positioner for dynamic listboxes */
DynamicListbox.Positioner = function(){};
DynamicListbox.Positioner.prototype.position = function(list_el, selector_el)
{
    var pos = new Object();
    
    var left     = getX(selector_el);
    var right    = left + list_el.offsetWidth;
    
    if(right > document.body.clientWidth)
        pos['right'] = '0px';
    else
        pos['left'] = left + 'px';

    pos['top'] = getY(selector_el) + selector_el.offsetHeight + 'px';    

    return pos;
}

/*
* This is an event type that is thrown when the user makes a selection in
* a DynamicListbox
*/
function DynamicListboxSelectionEvent(listbox_object)
{
    this.listbox_object = listbox_object;
}
DynamicListboxSelectionEvent.prototype = new AJSEvent(LISTBOX_SELECTION_EVENT);

