

// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var 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.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
};

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + 0.5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
    },
    pulse: function(pos, pulses) { 
      pulses = pulses || 5; 
      return (
        ((pos % (1/pulses)) * pulses).round() == 0 ? 
              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
        );
    },
    spring: function(pos) { 
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') || 
        Object.isFunction(element)) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || { });
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || { });
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable, {
  initialize: function() {
    this.effects  = [];
    this.interval = null;    
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = Object.isString(effect.options.queue) ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if (!this.interval)
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if (this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++) 
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;
    
    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;
    
    eval('this.render = function(pos){ '+
      'if (this.state=="idle"){this.state="running";'+
      codeForEvent(this.options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(this.options,'afterSetup')+
      '};if (this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(this.options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(this.options,'afterUpdate')+
      '}}');
    
    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if (timePos >= this.startOn) {
      if (timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if (effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(), 
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) : 
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || { });
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};

Effect.Scale = Class.create(Effect.Base, {
  initialize: function(element, percent) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || { });
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = { };
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if (fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if (this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if (/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if (!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = { };
    if (this.options.scaleX) d.width = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + 'px';
    if (this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute') {
        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if (this.options.scaleY) d.top = -topd + 'px';
        if (this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = { };
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if (!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if (!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
    scrollOffsets = document.viewport.getScrollOffsets(),
    elementOffsets = $(element).cumulativeOffset(),
    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1] > max ? max : elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()) }
  );
};

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from: element.getOpacity() || 1.0,
    to:   0.0,
    afterFinishInternal: function(effect) { 
      if (effect.options.to!=0) return;
      effect.element.hide().setStyle({opacity: oldOpacity}); 
    }
  }, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element)
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || { })
   );
};

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || { })
  );
};

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || { }));
};

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      })
    }
  }, arguments[1] || { }));
};

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        } 
      }, arguments[1] || { }));
};

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
};

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, { 
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping(); 
    }
  });
};

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
             }
           }, options)
      )
    }
  });
};

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { };
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });
    
    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        }
      }
    }
    this.start(options);
  },
  
  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if (property == 'opacity') {
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if (Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return { 
        style: property.camelize(), 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      )
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] = 
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     track.keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }
  
  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]); 
  });
  
  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
};

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element)
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) { 
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    }
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);

// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { }
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element)
    this.element     = element;
    this.update      = $(update);
    this.hasFocus    = false;
    this.changed     = false;
    this.active      = false;
    this.index       = 0;
    this.entryCount  = 0;
    this.oldElementValue = this.element.value;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || { };

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow ||
      function(element, update){
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false,
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide ||
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
     else
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer =
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex)
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },

  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },

  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ?
          Element.addClassName(this.getEntry(i),"selected") :
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) {
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },

  markPrevious: function() {
    if(this.index > 0) this.index--
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },

  markNext: function() {
    if(this.index < this.entryCount-1) this.index++
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },

  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },

  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },

  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();

    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount =
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else {
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;

      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();

    var entry = encodeURIComponent(this.options.paramName) + '=' +
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams)
      this.options.parameters += '&' + this.options.defaultParams;

    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&
          ret.length < instance.options.choices ; i++) {

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ?
            elem.toLowerCase().indexOf(entry.toLowerCase()) :
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) {
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars &&
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ?
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
}

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML;
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw 'Server returned an invalid collection representation.';
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element);
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});

 var recordingStartTime = null;
 var recordedElementId = null;

 function startRecording(event) {
  var element = event.target;
  recordingStartTime = new Date();
  recordedElementId = element.id;
 }

 function stopRecording(event) {
  var element = event.target;
  var recordingStopTime = new Date();
  var duration = (recordingStopTime - recordingStartTime);
  if (recordedElementId != element.id) {
		   //alert("different element " + element.id + " != " + recordedElementId);
  }
  else {
	  if (duration > 1000 && recordedElementId == element.id) {
		   //alert('recording stopped: ' + recordingStopTime + ", duration = " + duration + ", for element " + element.id);
			// var userActionAjax = new sack();
			// userActionAjax.method = 'GET';
			// userActionAjax.setVar("action", 'analysis-hover');
			// userActionAjax.setVar("duration", duration);
			// userActionAjax.setVar("src", element.id);
			// userActionAjax.requestFile = 'userAction.jsp';
			// userActionAjax.runAJAX('');

		   //pageTracker._trackPageview('odp mouseover ' + duration + " ms");
		   pageTracker._trackPageview('/tracking/odp mouseover');
	  }
	  if (duration <= 1000) {
	   //alert("ignored, duration = " + duration);
	  }
  }

  recordingStartTime = null;
  recordedElementId = null;
 }

 function registerHoverRecorder(elementId, callback) {
  var element = document.getElementById(elementId);

  if (element != null) {
   element.addEventListener('mouseover', startRecording, false);
   element.addEventListener('mouseout', stopRecording, false);
  }
 }

 function box(filled, size, offset) {
   document.write('<div class="'
       + (filled ? 'filled-box' : 'empty-box')
       + '" style="width: ' + size
       + '; height: ' + size
       + '; left: '
       + offset
       + 'px;"></div>');
}
function checklist(numChecked, totalBoxes) {
               var boxSize = 9;
               var totalWidth = (boxSize+1) * totalBoxes - 1;

               document.write('<div style="text-align: center;">');
               document.write('<div class="checklist" style="height: ' + boxSize +
'px; width: ' + totalWidth + 'px;">');
               for (i = 0; i < totalBoxes; i++) {
                       box(i < numChecked, boxSize, i * (boxSize+1));
               }
               document.write('</div>');
               document.write('<div class="checklist-text">' + numChecked + ' of '
+ totalBoxes + ' found</div>');
               document.write('</div>');
   }
// function userAction(action) {
// var userActionAjax = new sack();
// userActionAjax.method = 'GET';
// userActionAjax.setVar("action", action);
// userActionAjax.requestFile = 'userAction.jsp';
// userActionAjax.runAJAX('');
// }
function imageNotFound(imageId, url, opacity) {
        //alert('onerror called');
        var img = document.getElementById(imageId);
        img.src = url;
        var ie = (document.all) ? 1 : 0;
        var p = (ie) ? "filter" : "MozOpacity";
        opacity = (ie) ? "alpha(opacity="+opacity+")" : opacity/100;
        img.style[p] = opacity;
       }

function fixImage(maxSize, imageId, waitRemaining) {
           var image = document.getElementById(imageId);
               if (image.removeAttribute) {
                       image.removeAttribute('width');
                       image.removeAttribute('height');
               }
			  //alert("3) " + image.src + ": height=" + image.height + ", width=" + image.width);
               var width = image.naturalWidth || image.width;
               var height = image.naturalHeight || image.height;
           	   //alert("4) " + image.src + ": height=" + image.height + ", width=" + image.width);
               if (height > width) {
                       if (height > maxSize) {
                               width = width * maxSize / height;
                               height = maxSize;
                       };
               } else {
                       if (width > maxSize) {
                               height = height * maxSize / width;
                               width = maxSize;
                       };
               };
               if (height<1 || width<1) {
                //image.style.display="none";
                //alert("image too small: height=" + height + ", width=" + width);
                //image.style.width=maxSize;
                //image.style.height='auto';

                if (waitRemaining > 0) {
                 waitRemaining = waitRemaining-1000;
                 //alert('waitRemaining: ' + waitRemaining);
                 setTimeout('fixImage("' + maxSize + '", "' + imageId + '", ' + waitRemaining + ')', 1000);
                }
                else {
                 image.style.width=maxSize;
                 image.style.height='auto';
                 //alert('time up');
                }
               }
               else {
                image.height = height;
                image.width = width;
                image.style.display="inline";
                image.style.visibility = "visible";
            	//alert("success) " + image.src + ": height=" + image.height + ", width=" + image.width);
               }
       }

function setStyleClassProperty(styleClass, propertyName, propertyValue) {
	var rules;
	if (document.all) {
		rules = 'rules';
	}
	else if (document.getElementById) {
		rules = 'cssRules';
	}
	for (var styleSheetIndex = 0; styleSheetIndex < document.styleSheets.length; styleSheetIndex++) {
		for (var ruleIndex = 0; ruleIndex < document.styleSheets[styleSheetIndex][rules].length; ruleIndex++) {
			if (document.styleSheets[styleSheetIndex][rules][ruleIndex].selectorText == styleClass) {
				document.styleSheets[styleSheetIndex][rules][ruleIndex].style[propertyName] = propertyValue;
			}
		}
	}
}
/* Simple AJAX Code-Kit (SACK) */
/* ?2005 Gregory Wild-Smith */
/* www.twilightuniverse.com */
/* Software licenced under a modified X11 licence, see documentation or authors website for more details */

function sack(file){
	this.AjaxFailedAlert = "Your browser does not support the enhanced functionality of this website, and therefore you will have an experience that differs from the intended one.\n";
	this.requestFile = file;
	this.method = "POST";
	this.URLString = "";
	this.encodeURIString = true;
	this.execute = false;

	this.onLoading = function() { };
	this.onLoaded = function() { };
	this.onInteractive = function() { };
	this.onCompletion = function() { };

	this.createAJAX = function() {
		try {
			this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (err) {
				this.xmlhttp = null;
			}
		}
		if(!this.xmlhttp && typeof XMLHttpRequest != "undefined")
			this.xmlhttp = new XMLHttpRequest();
		if (!this.xmlhttp){
			this.failed = true; 
		}
	};
	
	this.setVar = function(name, value){
		if (this.URLString.length < 3){
			this.URLString = name + "=" + value;
		} else {
			this.URLString += "&" + name + "=" + value;
		}
	}
	
	this.encVar = function(name, value){
		var varString = encodeURIComponent(name) + "=" + encodeURIComponent(value);
	return varString;
	}
	
	this.encodeURLString = function(string){
		varArray = string.split('&');
		for (i = 0; i < varArray.length; i++){
			urlVars = varArray[i].split('=');
			if (urlVars[0].indexOf('amp;') != -1){
				urlVars[0] = urlVars[0].substring(4);
			}
			varArray[i] = this.encVar(urlVars[0],urlVars[1]);
		}
	return varArray.join('&');
	}
	
	this.runResponse = function(){
		eval(this.response);
	}
	
	this.runAJAX = function(urlstring){
		this.responseStatus = new Array(2);
		if(this.failed && this.AjaxFailedAlert){ 
			alert(this.AjaxFailedAlert); 
		} else {
			if (urlstring){ 
				if (this.URLString.length){
					this.URLString = this.URLString + "&" + urlstring; 
				} else {
					this.URLString = urlstring; 
				}
			}
			if (this.encodeURIString){
				var timeval = new Date().getTime(); 
				this.URLString = this.encodeURLString(this.URLString);
				this.setVar("rndval", timeval);
			}
			if (this.element) { this.elementObj = document.getElementById(this.element); }
			if (this.xmlhttp) {
				var self = this;
				if (this.method == "GET") {
					var totalurlstring = this.requestFile + "?" + this.URLString;
					this.xmlhttp.open(this.method, totalurlstring, true);
				} else {
					this.xmlhttp.open(this.method, this.requestFile, true);
				}
				if (this.method == "POST"){
  					try {
						this.xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded')  
					} catch (e) {}
				}

				// this.xmlhttp.send(this.URLString);
				this.xmlhttp.onreadystatechange = function() {
					switch (self.xmlhttp.readyState){
						case 1:
							self.onLoading();
						break;
						case 2:
							self.onLoaded();
						break;
						case 3:
							self.onInteractive();
						break;
						case 4:
							self.response = self.xmlhttp.responseText;
							self.responseXML = self.xmlhttp.responseXML;
							self.responseStatus[0] = self.xmlhttp.status;
							self.responseStatus[1] = self.xmlhttp.statusText;
							self.onCompletion();
							if(self.execute){ self.runResponse(); }
							if (self.elementObj) {
								var elemNodeName = self.elementObj.nodeName;
								elemNodeName.toLowerCase();
								if (elemNodeName == "input" || elemNodeName == "select" || elemNodeName == "option" || elemNodeName == "textarea"){
									self.elementObj.value = self.response;
								} else {
									self.elementObj.innerHTML = self.response;
								}
							}
							self.URLString = "";
						break;
					}
				};
				this.xmlhttp.send(this.URLString);
			}
		}
	};
this.createAJAX();
}
//-----------------------------------------------------------
//	
//	WBKAjax.js class
//
//		Authur: Bo Yuan
//
//		All rights reserved (c) 2005 - 2010 WDB2 Inc.
//			
//-----------------------------------------------------------

function WBKObjectInheritance(parent, child)
{
    var property; 
    for(property in parent)
    {
            if(!child[property])
            {
                    child[property] = parent[property];
            }
    }
}

function WBKAjaxManager()
{
    var m_xmlhttp = null;
    var m_ajaxArray = new Array();
    var m_ajaxNextID = 0;

    if( null == m_xmlhttp )
    {
        if (window.ActiveXObject)
        {
                m_xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        else if (window.XMLHttpRequest)
        {
                m_xmlhttp = new XMLHttpRequest();                
        }
    }

    this.getXMLHttp = function() { return m_xmlhttp; }

    this.addAjax = function(theAjax)
    {
            var ajaxid = m_ajaxNextID++;
            m_ajaxArray[ajaxid] = theAjax;

            return ajaxid;
    }

    this.getAjax = function(ajaxid)
    {
            var theAjax = m_ajaxArray[ajaxid];
            return theAjax;
    }

    this.removeAjax = function(ajaxid)
    {
            m_ajaxArray[ajaxid] = null;
    }
}
theWBKAjaxManager = new WBKAjaxManager();

function WBKAjaxOnloadCleaner()
{
    this.Cleanup = function()
    {
    }
}

function WBKAjaxOnloadManager()
{
    var m_ajaxidArray = new Array();
    var m_ajaxInuseArray = new Array();
    var m_InuseCount = 0;
    var m_cleaner = null;

    this.addAjax = function(theAjax)
    {
        var ajaxid = theAjax.getID();
        var size = m_ajaxidArray.length;
        m_ajaxidArray[size] = ajaxid;
    }

    this.removeAjax = function(ajaxid)
    {
        if( m_InuseCount > 0 && m_ajaxInuseArray[ajaxid] )
        {
            m_InuseCount--;
            //alert("inuse count" + m_InuseCount);
            if( m_InuseCount <= 0 && null != m_cleaner )
            {
                    m_cleaner.Cleanup();
            }
        }
    }

    this.setCleaner = function( theCleaner )
    {
        m_cleaner = theCleaner;
    }

    this.doAjax = function()
    {
        var size = m_ajaxidArray.length;
        for(var i = 0; i < size; i++)
        {
                var ajaxid = m_ajaxidArray[i];
                var theAjax = theWBKAjaxManager.getAjax(ajaxid);
                if( theAjax )
                {
                        m_ajaxInuseArray[ajaxid] = true;
                        m_InuseCount++;
                        theAjax.doAjax();
                }
        }
    }
}
theWBKAjaxOnloadManager = new WBKAjaxOnloadManager();

function WBKAjax(processor, requestmethod, responsemethod) {
    var m_xmlhttp = null;
    var m_ajaxid = -1;
    var m_autoDelete = true;
    var m_bDebuging = false;

    var m_processor = processor;
    var m_parameters = null;//the name-value map
    var m_requestMethod = requestmethod;
    var m_responseMethod = responsemethod;

    this.registerAjax = function(theAjax)
    {
        m_ajaxid = theWBKAjaxManager.addAjax(theAjax);
        m_xmlhttp = theWBKAjaxManager.getXMLHttp();
    }
    
    this.enableDebuging = function() { m_bDebuging = true; }

    this.getID = function()
    {
        return m_ajaxid;
    }

    this.addParameter = function(aname, avalue) {
        if( null == m_parameters ) {
            m_parameters = new Object();
        }
        m_parameters[aname] = avalue;
    }

    this.getParameter = function(aname) {
        if( null != m_parameters )
            return m_parameters[aname];
        else
            return 'undefined';
    }

    this.sendXMLHttpRequest = function (page, requestIn) {
        var url = page;
        var request = requestIn;
        if( 'GET' == m_requestMethod )
        {
                url += "?" + request;
                request = null;
        }
        m_xmlhttp.open(m_requestMethod, url, true);
        m_xmlhttp.onreadystatechange = this.callbackEntry;
        m_xmlhttp.send(request);
    }

    this.callbackEntry = function () {
        if( m_xmlhttp.readyState == 4 && m_xmlhttp.status == 200 )
	{
            var theAjax = theWBKAjaxManager.getAjax(m_ajaxid);
            if( theAjax )
            {
				if( m_responseMethod == "XML")
					theAjax.ajaxCallback(m_xmlhttp.responseXML);
				else
					theAjax.ajaxCallback(m_xmlhttp.responseText);
					
	            if( m_autoDelete ) {
		            theWBKAjaxManager.removeAjax(m_ajaxid);
		            theWBKAjaxOnloadManager.removeAjax(m_ajaxid);
	            }
            }
        }
    }
    
    this.clearMe = function () {
		theWBKAjaxManager.removeAjax(m_ajaxid);
		theWBKAjaxOnloadManager.removeAjax(m_ajaxid);
    }

    this.ajaxCallback = function(data)
    {
        alert("ajaxCallback is not implemented");
    }

    this.doAjax2 = function(AdditionalRequest)
    {
        var request = AdditionalRequest;
        
        if( null != m_parameters) {
			var sep = "";
	        if(request.length > 0 )
	        	sep = "&";
            for(var aname in m_parameters) {
                request += sep + aname;
                request += "=" + m_parameters[aname];
                sep = "&";
            }
        }
        
        if( m_bDebuging )
	        alert(request);
	        
        this.sendXMLHttpRequest(m_processor, request);
    }

    this.doAjax = function()
    {
    	this.doAjax2("");
    }
}

var ajax = new sack();
ajax.encodeURIString = false;
	    
function formData2QueryString(docForm) {
	var strSubmitContent = '';
	var formElem;
	var strLastElemName = '';
	
	for (i = 0; i < docForm.elements.length; i++) {		
		formElem = docForm.elements[i];
		switch (formElem.type) {
			// Text fields, hidden form elements
			case 'text':
			case 'hidden':
			case 'password':
			case 'textarea':
			case 'select-one':
				strSubmitContent += formElem.name + '=' + escape(formElem.value) + '&'
				break;
				
			// Radio buttons
			case 'radio':
				if (formElem.checked) {
					strSubmitContent += formElem.name + '=' + escape(formElem.value) + '&'
				}
				break;
				
			// Checkboxes
			case 'checkbox':
				if (formElem.checked) {
					// Continuing multiple, same-name checkboxes
					if (formElem.name == strLastElemName) {
						// Strip of end ampersand if there is one
						if (strSubmitContent.lastIndexOf('&') == strSubmitContent.length-1) {
							strSubmitContent = strSubmitContent.substr(0, strSubmitContent.length - 1);
						}
						// Append value as comma-delimited string
						strSubmitContent += ',' + escape(formElem.value);
					}
					else {
						strSubmitContent += formElem.name + '=' + escape(formElem.value);
					}
					strSubmitContent += '&';
				}
				break;
				
		}
		strLastElemName = formElem.name
	}
	
	// Remove trailing separator
	strSubmitContent = strSubmitContent.substr(0, strSubmitContent.length - 1);
	return strSubmitContent;
}

function formData2QueryStringWithExtraAttributes(docForm, extraAttributeName, extraAttributeValues) {
	body = formData2QueryString(docForm);

	if (typeof extraAttributeValues != 'undefined') {
		for (i = 0; i < extraAttributeValues.length; i++) {
		    var v = extraAttributeValues[i];
			body += '&' + extraAttributeName + '=' + escape(v);
		}
	}
	return body;
}

function updateSearchFormElement(f, key) {
    if (typeof f == 'undefined' || typeof f[key] == 'undefined') {
    	return;
    }
    
    for (var i=0; i < f[key].options.length; i++) { // for each value of the key
		var option = f[key].options[i];
		var value = option.value;
	
		var eid = key + "_" + value
	
		var count = keyValueCountMap[eid];
		if (count == null || count == 'undefined') {
			count = 0;
		}
	
		var regex = new RegExp(" \\(.+\\)$|$", "i");
		var replacementStr;
		
		// look for server errors
		if (ajax.responseStatus[0] == '200') {
			replacementStr =  "(" + count + ")";
		}
		else {
			replacementStr = "";
		}
		
		option.text = new String(option.text).replace(regex, " " + replacementStr);
	
		if (ajax.responseStatus[0] == '200') {
			if (count == 0) {
			    // set a special style
			    option.className = key + "-none";
			}
			else {
			    option.className = "";
			}
		}
		else {
			option.className = "serverError";
		}
    }
}

var keyValueCountMap = new Object();
// key_value -> count

// {min, max, count}
var priceRanges;
// {min, max, count}
var mileageRanges;

function beforeAjax() {
	if ($("loading-price") != null) {
		Effect.Appear("loading-price");
	}
	if ($("loading-mileage") != null) {
		Effect.Appear("loading-mileage");
	}
}

function afterAjax() {
	new Effect.Highlight("location");
	new Effect.Highlight("make");
	new Effect.Highlight("model");	
	if ($("priceRanges") != null) {
		new Effect.Highlight("priceRanges");
	}
	if ($("mileageRanges") != null) {
		new Effect.Highlight("mileageRanges");
	}
	if ($("loading-price") != null) {
		Effect.Fade("loading-price");
	}
	if ($("loading-mileage") != null) {
		Effect.Fade("loading-mileage");
	}
}

function whenInteractive() {
	//alert("interactive");
}

function whenCompleted() {
	//alert("completed");
    // go through all element which are 'count'
    var counts = ajax.responseXML.getElementsByTagName('count');
    //alert('total counts are ' + counts.length);
    
    for (var i = 0; i < counts.length; i++) {
		var countElem = counts.item(i);
		var key = countElem.getAttribute("key");
		var value = countElem.getAttribute("value");
		var count = countElem.getAttribute("count");
		keyValueCountMap[key + "_" + value] = count;
    }
    
    // now go through all elements which are for price ranges
    var priceRangeElements = ajax.responseXML.getElementsByTagName('priceRange');
    priceRanges = new Array();
    for (var i = 0; i < priceRangeElements.length; i++) {
     	var rangeElem = priceRangeElements[i];
		var min = rangeElem.getAttribute("min");
		var max = rangeElem.getAttribute("max");
		var label = rangeElem.getAttribute("label");
	  	var priceRange = new Object(); 
	  	priceRange.min = min;
	  	priceRange.max = max;
	  	priceRange.label = label;
		priceRanges.push(priceRange);
    }
    
    // now go through all elements which are for mileage ranges
    var mileageRangeElements = ajax.responseXML.getElementsByTagName('mileageRange');
    mileageRanges = new Array();
    //alert(mileageRangeElements.length + " mileage range elements found");
    for (var i = 0; i < mileageRangeElements.length; i++) {
     	var rangeElem = mileageRangeElements[i];
		var min = rangeElem.getAttribute("min");
		var max = rangeElem.getAttribute("max");
		var label = rangeElem.getAttribute("label");
	  	var range = new Object(); 
	  	range.min = min;
	  	range.max = max;
	  	range.label = label;
		mileageRanges.push(range);
    }
    
    updateSearchFormElement(document.forms["carSearchForm"], "location");
    updateSearchFormElement(document.forms["carSearchForm"], "make");
    updateSearchFormElement(document.forms["carSearchForm"], "model");

    // website input form    
    //updateSearchFormElement(document.forms["websiteForm"], "website");
    
    updatePriceRanges();
    updateMileageRanges();
    // afterAjax(); // deactivated for now
}

function updatePriceRanges() {
    var inner = "";
     	for (var i = 0; i < priceRanges.length; i++) {
     		var priceRange = priceRanges[i];
     		var min = priceRange.min;
     		var max = priceRange.max;
     		var label = priceRange.label;

		    inner += "<a class='split-range' " 
		    		+ " href='javascript:void(0)' "  
    		        + " onClick=\"fillPriceRange('" + min + "', '" + max + "')\" "
    		        + ">"
    		        + label 
    		        + "</a>	<br/>";
		}
		
		var pr = $("priceRanges");
		if (pr != null) {
			pr.innerHTML = inner;
		}
}
function updateMileageRanges() {
    var inner = "";
     	for (var i = 0; i < mileageRanges.length; i++) {
     		var mileageRange = mileageRanges[i];
     		var min = mileageRange.min;
     		var max = mileageRange.max;
     		var label = mileageRange.label;

		    inner += "<a class='split-range' " 
		    		+ " href='javascript:void(0)' "  
    		        + " onClick=\"fillMileageRange('" + min + "', '" + max + "')\" "
    		        + ">"
    		        + label 
    		        + "</a>	<br/>";
		}
		
		var pr = $("mileageRanges");
		if (pr != null) {
			pr.innerHTML = inner;
		}
}

function updateSearchForm(attributes) {
    var target = 'car.count.sscache';
    //var target = 'car.count';
       
    // request body
    var body = formData2QueryStringWithExtraAttributes(document.forms["carSearchForm"], "attribute", attributes);
    
	// ajax.setVar("myExampleVar", "myExampleData"); // alternate method of setting data to be parsed.
	ajax.requestFile = target;
	ajax.method = "GET";
	// ajax.onLoading = whenLoading;
	// ajax.onLoaded = whenLoaded;
	// ajax.onInteractive = whenInteractive;
	ajax.onCompletion = whenCompleted;
	// beforeAjax(); - deactivated for now
	// ajax.runAJAX(body);	- deactivated for now, till group counts are hooked up to lucene	
}

function toggleAdditionalOptions() {
    var e = document.forms["carSearchForm"].elements["additionalOptions"];
 	var f = e.form;
 	var hidden = e.checked;
 	if (hidden) {
 		document.getElementById("additionalSearchOptionsTable").style.visibility = "visible";
 	}
 	else {
 		document.getElementById("additionalSearchOptionsTable").style.visibility = "hidden";
 	}
}

function quote(v)
{
    return '"' + v + '"';
}

function quotedList(arr) {
	if (typeof arr == 'undefined') return "";
	quoted = arr.map(quote);
	return quoted.join(',');
}

    function drawMakeControl(make) {
	document.writeln("<select name='make' onChange='makeChanged()'>");
	for (var j = 0; j < makesData.length; j++) {
	  var makeDescriptor = new Object(); 
	  	makeDescriptor.id = makesData[j][0]; 
	  	makeDescriptor.name = makesData[j][1];
	  	document.writeln("<option value=\"" + makeDescriptor.id + "\">" + makeDescriptor.name + "</option>");
	}
	document.writeln("</select>");
    document.forms["carSearchForm"].make.value = make;
    }
    
        function updateModel(md) {
        // update document.forms.carSearchForm.model array and the current value
		modelDrop = document.forms.carSearchForm.model;
		// remove all current entries
		modelDrop.length = 0;
		// get all models for the current make
		var currentMake = document.forms.carSearchForm.make.value;
		var makeDescriptor = allMakes[currentMake];
		var allModels = makeDescriptor.models;
		for (var i = 0; i < allModels.length; i++) {
		    var model = allModels[i];
		    modelDrop.options[i]=new Option(model.displayName,model.id);
	            if (model.id == md) {
					modelDrop.options[i].selected = true;
	            }
		}
    }

    function makeChanged() {
    	// reset model
		updateModel('');
		updateSearchForm(['model']);
    }
    
    function fillPriceRange(p1, p2) {
    	document.forms.carSearchForm.minPrice.value = p1;
    	document.forms.carSearchForm.maxPrice.value = p2;
		priceChanged();
    }

    function fillMileageRange(p1, p2) {
    	document.forms.carSearchForm.minMileage.value = p1;
    	document.forms.carSearchForm.maxMileage.value = p2;
		updateSearchForm([]);
    }
    
    function drawModelControl(md) {
    	document.writeln("<select name='model' onChange='modelChanged()'>");
		document.writeln("</select>");

    	// update model with the given make initially
    	updateModel(md);
    }
    
   	function page(n) {
		document.forms["carSearchForm"].pageNumber.value = n;
		document.forms["carSearchForm"].submit();
	}
	
	function sortBy(value) {
		document.forms["carSearchForm"].sortBy.value = value;
		page(1);
	}
    
    					function sortBy(attribute, isAsc) {
						form = document.forms["carSearchForm"];
						form.sortBy.value = attribute;
						form.submit();
					}
    
function hideElement(id) {
	document.getElementById(id).style.display = 'none';
}
function showElement(id) {
	document.getElementById(id).style.display = 'block';	
}
function drawPriceComparision(
height, length, minLabel, topLabelClass, priceLabelOffset, priceLabel, 
newPrice, avgLabelOffset, avgLabel, newAvg, maxLabel, priceDiffClass, priceDiffPosition, priceDiffLength) {
document.writeln("<div style='height: " + height + "px;'></div>");
document.writeln("<div style='width: " +  length + ";' class='range-axis'>");
document.writeln(" <div class='range-min-label'>" + minLabel + "</div>");
document.writeln(" <div class='range-min-mark'></div>");
document.writeln(" <div class='" + topLabelClass + "' style='left: " + (newPrice + priceLabelOffset) + "px;'>" + priceLabel + "</div>");
document.writeln(" <div class='range-topmark' style='left: " + newPrice + "px;'></div>");
document.writeln(" <div class='range-mid-label' style='left: " + (newAvg + avgLabelOffset) + "px;'>" + avgLabel + "</div>");
document.writeln(" <div class='range-price-mark' style='left: " + (newPrice-4) + "px;'></div>");
document.writeln(" <div class='range-mid-mark' style='left: " + newAvg + "px;'></div>");
document.writeln(" <div style='left: " + (length - 20) + "px;' class='range-max-label'>" + maxLabel + "</div>");
document.writeln(" <div style='left: " + (length - 1) + "px;' class='range-max-mark'></div>");
document.writeln(" <div class='" + priceDiffClass + "' style='left: " + priceDiffPosition + "px; width: " + priceDiffLength + "px;'>");
document.writeln(" </div>");
document.writeln("</div>");
document.writeln("<br/>");
document.writeln("<br/>");
}

function printReviews(year, make, model)
{
var expertReviewLink = "http://www.google.com/search?q=" + escape(year) + "+" + escape(make) + "+" + escape(model) + "+reviews";
var userReviewLink = "http://www.google.com/search?q=" + escape(year) + "+" + escape(make) + "+" + escape(model) + "+\"user+reviews\"";
document.writeln("<img width=3 height=3 src='images/bullet.gif' border='0'/>&nbsp;<a class='review' href='" + expertReviewLink + "' target='reviewWindow'>Expert Reviews</a>&nbsp;");
document.writeln("<img width=3 height=3 src='images/bullet.gif' border='0'/>&nbsp;<a class='review' href='" + userReviewLink + "' target='reviewWindow'>User Reviews</a>&nbsp;");
}

function printCarFax(vin) {
 document.writeln("<img width=3 height=3 src='images/bullet.gif' border='0'/>&nbsp;<a class=history href='http://carfax.com/cfm/check_order.cfm?vin=" + vin + "&PopUpStatus=0'>Car History</a>");
}


function printTotalCount() {
 document.writeln("2,008,552");
}




function selectLocation() {



document.writeln("<select name='location' onChange='locationChanged()' size='1'>");
 
   document.writeln("<option value='0' ></option>");
 
   document.writeln("<option value='10' >Atlanta, GA</option>");
 
   document.writeln("<option value='14' >Austin, TX</option>");
 
   document.writeln("<option value='58' >Baltimore, MD</option>");
 
   document.writeln("<option value='1' >Boston, MA</option>");
 
   document.writeln("<option value='15' >Chicago, IL</option>");
 
   document.writeln("<option value='23' >Cleveland, OH</option>");
 
   document.writeln("<option value='33' >Columbus, OH</option>");
 
   document.writeln("<option value='8' >Dallas, TX</option>");
 
   document.writeln("<option value='17' >Denver, CO</option>");
 
   document.writeln("<option value='22' >Detroit, MI</option>");
 
   document.writeln("<option value='7' >Hartford, CT</option>");
 
   document.writeln("<option value='9' >Houston, TX</option>");
 
   document.writeln("<option value='24' >Indianapolis, IN</option>");
 
   document.writeln("<option value='27' >Kansas City, MO</option>");
 
   document.writeln("<option value='19' >Las Vegas, NV</option>");
 
   document.writeln("<option value='5' >Los Angeles, CA</option>");
 
   document.writeln("<option value='25' >Memphis, TN</option>");
 
   document.writeln("<option value='16' >Miami, FL</option>");
 
   document.writeln("<option value='26' >Milwaukee, WI</option>");
 
   document.writeln("<option value='18' >Minneapolis, MN</option>");
 
   document.writeln("<option value='2' >New York, NY</option>");
 
   document.writeln("<option value='3' >Philadelphia, PA</option>");
 
   document.writeln("<option value='11' >Phoenix, AZ</option>");
 
   document.writeln("<option value='29' >Pittsburgh, PA</option>");
 
   document.writeln("<option value='20' >Portland, OR</option>");
 
   document.writeln("<option value='21' >Raleigh, NC</option>");
 
   document.writeln("<option value='30' >Saint Louis, MO</option>");
 
   document.writeln("<option value='28' >Salt Lake City, UT</option>");
 
   document.writeln("<option value='13' >San Diego, CA</option>");
 
   document.writeln("<option value='4' >San Francisco, CA</option>");
 
   document.writeln("<option value='6' >Seattle, WA</option>");
 
   document.writeln("<option value='31' >Tampa, FL</option>");
 
   document.writeln("<option value='12' >Washington, DC</option>");
 
document.writeln("</select>");
}


function selectDistance() {



document.writeln("<select name='distance' onChange='distanceChanged()' size='1'>");
 
   document.writeln("<option value='15' >15 miles</option>");
 
   document.writeln("<option value='30' >30 miles</option>");
 
   document.writeln("<option value='45' >45 miles</option>");
 
   document.writeln("<option value='60' >60 miles</option>");
 
   document.writeln("<option value='90' >90 miles</option>");
 
   document.writeln("<option value='120' >120 miles</option>");
 
   document.writeln("<option value='150' >150 miles</option>");
 
   document.writeln("<option value='200' >200 miles</option>");
 
   document.writeln("<option value='300' >300 miles</option>");
 
   document.writeln("<option value='500' >500 miles</option>");
 
   document.writeln("<option value='800' >800 miles</option>");
 
   document.writeln("<option value='1500' >1500 miles</option>");
 
   document.writeln("<option value='1000000' >Any</option>");
 
document.writeln("</select>");
}


function selectMaxAge() {



document.writeln("<select name='maxAge' onChange='maxAgeChanged()' size='1'>");
 
   document.writeln("<option value='' selected>All</option>");
 
   document.writeln("<option value='2' >Last 2 days</option>");
 
   document.writeln("<option value='7' >Last 7 days</option>");
 
   document.writeln("<option value='14' >Last 14 days</option>");
 
   document.writeln("<option value='21' >Last 21 days</option>");
 
   document.writeln("<option value='30' >Last 30 days</option>");
 
   document.writeln("<option value='90' >Last 90 days</option>");
 
document.writeln("</select>");
}

function selectSellerType() {



document.writeln("<select name='sellerType' onChange='sellerTypeChanged()' size='1'>");
 
   document.writeln("<option value='' selected>All</option>");
 
   document.writeln("<option value='PRIVATE' >Owner</option>");
 
   document.writeln("<option value='DEALER' >Dealer</option>");
 
document.writeln("</select>");
}

function selectTransmissionType() {



document.writeln("<select name='transmissionType' onChange='transmissionTypeChanged()' size='1'>");
 
   document.writeln("<option value='' selected>All</option>");
 
   document.writeln("<option value='AUTOMATIC' >Automatic</option>");
 
   document.writeln("<option value='MANUAL' >Manual</option>");
 
document.writeln("</select>");
}





var allMakes = new Object(); 
var makesData = new Array (
['', 'All Makes', new Array(['', 'All Models'])],
['acura', 'Acura', new Array (['', 'All Models'],['cl', 'CL'],['integra', 'Integra'],['legend', 'Legend'],['mdx', 'MDX'],['nsx', 'NSX'],['rdx', 'RDX'],['rl', 'RL'],['rsx', 'RSX'],['slx', 'SLX'],['tl', 'TL'],['tsx', 'TSX'],['vigor', 'Vigor'],['other models', 'Other Models'])],
['audi', 'Audi', new Array (['', 'All Models'],['100', '100'],['200', '200'],['80', '80'],['90', '90'],['a3', 'A3'],['a4', 'A4'],['a5', 'A5'],['a6', 'A6'],['a8', 'A8'],['allroad quattro', 'Allroad Quattro'],['cabriolet', 'Cabriolet'],['coupe', 'Coupe'],['q7', 'Q7'],['r8', 'R8'],['rs4', 'RS4'],['rs 6', 'RS 6'],['s4', 'S4'],['s5', 'S5'],['s6', 'S6'],['s8', 'S8'],['tt', 'TT'],['v8', 'V8'],['other models', 'Other Models'])],
['bmw', 'BMW', new Array (['', 'All Models'],['1 series', '1 Series'],['3 series', '3 Series'],['5 series', '5 Series'],['6 series', '6 Series'],['7 series', '7 Series'],['8 series', '8 Series'],['alpina', 'Alpina'],['alpina b7', 'Alpina B7'],['m', 'M'],['m3', 'M3'],['m5', 'M5'],['m6', 'M6'],['x3', 'X3'],['x5', 'X5'],['z3', 'Z3'],['z4', 'Z4'],['z8', 'Z8'],['other models', 'Other Models'])],
['buick', 'Buick', new Array (['', 'All Models'],['century', 'Century'],['electra', 'Electra'],['enclave', 'Enclave'],['estate wagon', 'Estate Wagon'],['lacrosse', 'LaCrosse'],['lesabre', 'LeSabre'],['lucerne', 'Lucerne'],['park avenue', 'Park Avenue'],['rainier', 'Rainier'],['reatta', 'Reatta'],['regal', 'Regal'],['rendezvous', 'Rendezvous'],['riviera', 'Riviera'],['roadmaster', 'Roadmaster'],['skylark', 'Skylark'],['terraza', 'Terraza'],['other models', 'Other Models'])],
['cadillac', 'Cadillac', new Array (['', 'All Models'],['allante', 'Allante'],['brougham', 'Brougham'],['catera', 'Catera'],['cts', 'CTS'],['cts-v', 'CTS-V'],['de ville', 'De Ville'],['dts', 'DTS'],['eldorado', 'Eldorado'],['escalade', 'Escalade'],['escalade esv', 'Escalade ESV'],['escalade ext', 'Escalade EXT'],['escalade hybrid', 'Escalade Hybrid'],['fleetwood', 'Fleetwood'],['seville', 'Seville'],['sixty special', 'Sixty Special'],['srx', 'SRX'],['sts', 'STS'],['sts-v', 'STS-V'],['xlr', 'XLR'],['xlr-v', 'XLR-V'],['other models', 'Other Models'])],
['chevrolet', 'Chevrolet', new Array (['', 'All Models'],['astro', 'Astro'],['astro cargo', 'Astro Cargo'],['avalanche', 'Avalanche'],['aveo', 'Aveo'],['beretta', 'Beretta'],['blazer', 'Blazer'],['c/k 1500 series', 'C/K 1500 Series'],['c/k 2500 series', 'C/K 2500 Series'],['c/k 3500 series', 'C/K 3500 Series'],['camaro', 'Camaro'],['caprice', 'Caprice'],['cavalier', 'Cavalier'],['celebrity', 'Celebrity'],['chevy van', 'Chevy Van'],['chevy van classic', 'Chevy Van Classic'],['classic', 'Classic'],['cobalt', 'Cobalt'],['colorado', 'Colorado'],['corsica', 'Corsica'],['corvette', 'Corvette'],['equinox', 'Equinox'],['express', 'Express'],['express cargo', 'Express Cargo'],['hhr', 'HHR'],['impala', 'Impala'],['lumina', 'Lumina'],['lumina minivan', 'Lumina Minivan'],['malibu', 'Malibu'],['malibu hybrid', 'Malibu Hybrid'],['malibu maxx', 'Malibu Maxx'],['metro', 'Metro'],['monte carlo', 'Monte Carlo'],['prizm', 'Prizm'],['r/v 3500 series', 'R/V 3500 Series'],['s-10', 'S-10'],['s-10 blazer', 'S-10 Blazer'],['silverado 1500', 'Silverado 1500'],['silverado 1500 classic', 'Silverado 1500 Classic'],['silverado 1500hd', 'Silverado 1500HD'],['silverado 1500hd classic', 'Silverado 1500HD Classic'],['silverado 1500 ss', 'Silverado 1500 SS'],['silverado 1500 ss classic', 'Silverado 1500 SS Classic'],['silverado 2500', 'Silverado 2500'],['silverado 2500hd', 'Silverado 2500HD'],['silverado 2500hd classic', 'Silverado 2500HD Classic'],['silverado 3500', 'Silverado 3500'],['silverado 3500 classic', 'Silverado 3500 Classic'],['silverado 3500hd', 'Silverado 3500HD'],['sportvan', 'Sportvan'],['ssr', 'SSR'],['suburban', 'Suburban'],['tahoe', 'Tahoe'],['tahoe hybrid', 'Tahoe Hybrid'],['tahoe limited/z71', 'Tahoe Limited/Z71'],['tracker', 'Tracker'],['trailblazer', 'TrailBlazer'],['trailblazer ext', 'TrailBlazer EXT'],['traverse', 'Traverse'],['uplander', 'Uplander'],['venture', 'Venture'],['other models', 'Other Models'])],
['chrysler', 'Chrysler', new Array (['', 'All Models'],['300', '300'],['300c srt-8', '300C SRT-8'],['300m', '300M'],['aspen', 'Aspen'],['cirrus', 'Cirrus'],['concorde', 'Concorde'],['crossfire', 'Crossfire'],['crossfire srt-6', 'Crossfire SRT-6'],['grand voyager', 'Grand Voyager'],['imperial', 'Imperial'],['le baron', 'Le Baron'],['lhs', 'LHS'],['new yorker', 'New Yorker'],['pacifica', 'Pacifica'],['prowler', 'Prowler'],['pt cruiser', 'PT Cruiser'],['sebring', 'Sebring'],['tc', 'TC'],['town and country', 'Town and Country'],['voyager', 'Voyager'],['other models', 'Other Models'])],
['daewoo', 'Daewoo', new Array (['', 'All Models'],['lanos', 'Lanos'],['leganza', 'Leganza'],['nubira', 'Nubira'],['other models', 'Other Models'])],
['dodge', 'Dodge', new Array (['', 'All Models'],['avenger', 'Avenger'],['caliber', 'Caliber'],['caravan', 'Caravan'],['challenger', 'Challenger'],['charger', 'Charger'],['charger srt-8', 'Charger SRT-8'],['colt', 'Colt'],['dakota', 'Dakota'],['daytona', 'Daytona'],['durango', 'Durango'],[' durango hybrid', ' Durango Hybrid'],['dynasty', 'Dynasty'],['grand caravan', 'Grand Caravan'],['intrepid', 'Intrepid'],['journey', 'Journey'],['magnum', 'Magnum'],['magnum srt-8', 'Magnum SRT-8'],['monaco', 'Monaco'],['neon', 'Neon'],['neon srt-4', 'Neon SRT-4'],['nitro', 'Nitro'],['omni', 'Omni'],['ram 150', 'RAM 150'],['ram 250', 'RAM 250'],['ram 350', 'RAM 350'],['ram 50 pickup', 'Ram 50 Pickup'],['ram cargo', 'Ram Cargo'],['ramcharger', 'Ramcharger'],['ram pickup 1500', 'Ram Pickup 1500'],['ram pickup 1500 srt-10', 'Ram Pickup 1500 SRT-10'],['ram pickup 2500', 'Ram Pickup 2500'],['ram pickup 3500', 'Ram Pickup 3500'],['ram van', 'Ram Van'],['ram wagon', 'Ram Wagon'],['shadow', 'Shadow'],['spirit', 'Spirit'],['sprinter', 'Sprinter'],['sprinter cargo', 'Sprinter Cargo'],['stealth', 'Stealth'],['stratus', 'Stratus'],['viper', 'Viper'],['other models', 'Other Models'])],
['eagle', 'Eagle', new Array (['', 'All Models'],['premier', 'Premier'],['summit', 'Summit'],['talon', 'Talon'],['vision', 'ViSion'],['other models', 'Other Models'])],
['ford', 'Ford', new Array (['', 'All Models'],['aerostar', 'Aerostar'],['aspire', 'Aspire'],['bronco', 'Bronco'],['bronco ii', 'Bronco II'],['contour', 'Contour'],['contour svt', 'Contour SVT'],['crown victoria', 'Crown Victoria'],['e-150', 'E-150'],['e-250', 'E-250'],['e-350', 'E-350'],['econoline cargo', 'Econoline Cargo'],['econoline wagon', 'Econoline Wagon'],['edge', 'Edge'],['escape', 'Escape'],['escape hybrid', 'Escape Hybrid'],['escort', 'Escort'],['excursion', 'Excursion'],['expedition', 'Expedition'],['expedition el', 'Expedition EL'],['explorer', 'Explorer'],['explorer sport', 'Explorer Sport'],['explorer sport trac', 'Explorer Sport Trac'],['f-150', 'F-150'],['f-150 heritage', 'F-150 HERITAGE'],['f-150 svt lightning', 'F-150 SVT LIGHTNING'],['f-250', 'F-250'],['f-250 super duty', 'F-250 Super Duty'],['f-350', 'F-350'],['f-350 super duty', 'F-350 Super Duty'],['f-450 super duty', 'F-450 Super Duty'],['festiva', 'Festiva'],['five hundred', 'Five Hundred'],['flex', 'Flex'],['focus', 'Focus'],['focus svt', 'Focus SVT'],['freestar', 'Freestar'],['freestyle', 'Freestyle'],['fusion', 'Fusion'],['gt', 'GT'],['ltd crown victoria', 'LTD Crown Victoria'],['mustang', 'Mustang'],['mustang svt cobra', 'Mustang SVT Cobra'],['probe', 'Probe'],['ranger', 'Ranger'],['shelby gt500', 'Shelby GT500'],['taurus', 'Taurus'],['taurus x', 'Taurus X'],['tempo', 'Tempo'],['thunderbird', 'Thunderbird'],['windstar', 'Windstar'],['windstar cargo', 'Windstar Cargo'],['other models', 'Other Models'])],
['geo', 'Geo', new Array (['', 'All Models'],['metro', 'Metro'],['prizm', 'Prizm'],['storm', 'Storm'],['tracker', 'Tracker'],['other models', 'Other Models'])],
['gmc', 'GMC', new Array (['', 'All Models'],['acadia', 'Acadia'],['c/k 2500 series', 'C/K 2500 Series'],['c/k 3500 series', 'C/K 3500 Series'],['canyon', 'Canyon'],['envoy', 'Envoy'],['envoy xl', 'Envoy XL'],['envoy xuv', 'Envoy XUV'],['jimmy', 'Jimmy'],['r/v 3500 series', 'R/V 3500 Series'],['rally wagon', 'Rally Wagon'],['s-15', 'S-15'],['s-15 jimmy', 'S-15 Jimmy'],['safari', 'Safari'],['safari cargo', 'Safari Cargo'],['savana', 'Savana'],['savana cargo', 'Savana Cargo'],['sierra 1500', 'Sierra 1500'],['sierra 1500hd', 'Sierra 1500HD'],['sierra 2500', 'Sierra 2500'],['sierra 2500hd', 'Sierra 2500HD'],['sierra 3500', 'Sierra 3500'],['sierra 3500hd', 'Sierra 3500HD'],['sierra c3', 'Sierra C3'],['sierra classic 1500', 'Sierra Classic 1500'],['sierra classic 2500', 'Sierra Classic 2500'],['sierra classic 3500', 'Sierra Classic 3500'],['sonoma', 'Sonoma'],['suburban', 'Suburban'],['syclone', 'Syclone'],['typhoon', 'Typhoon'],['vandura', 'Vandura'],['yukon', 'Yukon'],['yukon denali', 'Yukon Denali'],['yukon hybrid', 'Yukon Hybrid'],['yukon xl', 'Yukon XL'],['other models', 'Other Models'])],
['honda', 'Honda', new Array (['', 'All Models'],['accord', 'Accord'],['civic', 'Civic'],['civic crx', 'Civic CRX'],['civic del sol', 'Civic del Sol'],['cr-v', 'CR-V'],['element', 'Element'],['fit', 'Fit'],['insight', 'Insight'],['odyssey', 'Odyssey'],['passport', 'Passport'],['pilot', 'Pilot'],['prelude', 'Prelude'],['ridgeline', 'Ridgeline'],['s2000', 'S2000'],['other models', 'Other Models'])],
['hummer', 'Hummer', new Array (['', 'All Models'],['h1', 'H1'],['h1 alpha', 'H1 Alpha'],['h2', 'H2'],['h2 sut', 'H2 SUT'],['h3', 'H3'],['other models', 'Other Models'])],
['hyundai', 'Hyundai', new Array (['', 'All Models'],['accent', 'Accent'],['azera', 'Azera'],['elantra', 'Elantra'],['entourage', 'Entourage'],['excel', 'Excel'],['genesis', 'Genesis'],['santa fe', 'Santa Fe'],['scoupe', 'Scoupe'],['sonata', 'Sonata'],['tiburon', 'Tiburon'],['tucson', 'Tucson'],['veracruz', 'Veracruz'],['xg300', 'XG300'],['xg350', 'XG350'],['other models', 'Other Models'])],
['infiniti', 'Infiniti', new Array (['', 'All Models'],['ex35', 'EX35'],['fx35', 'FX35'],['fx45', 'FX45'],['fx50', 'FX50'],['g20', 'G20'],['g35', 'G35'],['g37', 'G37'],['i30', 'I30'],['i35', 'I35'],['j30', 'J30'],['m30', 'M30'],['m35', 'M35'],['m45', 'M45'],['q45', 'Q45'],['qx4', 'QX4'],['qx56', 'QX56'],['other models', 'Other Models'])],
['isuzu', 'Isuzu', new Array (['', 'All Models'],['amigo', 'Amigo'],['ascender', 'Ascender'],['axiom', 'Axiom'],['hombre', 'Hombre'],['i-series', 'i-Series'],['impulse', 'Impulse'],['oasis', 'Oasis'],['pickup', 'Pickup'],['rodeo', 'Rodeo'],['rodeo sport', 'Rodeo Sport'],['stylus', 'Stylus'],['trooper', 'Trooper'],['vehicross', 'VehiCross'],['other models', 'Other Models'])],
['jaguar', 'Jaguar', new Array (['', 'All Models'],['s-type', 'S-Type'],['s-type r', 'S-Type R'],['x-type', 'X-Type'],['xj-series', 'XJ-Series'],['xjr', 'XJR'],['xk-series', 'XK-Series'],['xkr', 'XKR'],['other models', 'Other Models'])],
['jeep', 'Jeep', new Array (['', 'All Models'],['cherokee', 'Cherokee'],['comanche', 'Comanche'],['commander', 'Commander'],['compass', 'Compass'],['grand cherokee', 'Grand Cherokee'],['grand cherokee srt-8', 'Grand Cherokee SRT-8'],['grand wagoneer', 'Grand Wagoneer'],['liberty', 'Liberty'],['patriot', 'Patriot'],['wagoneer', 'Wagoneer'],['wrangler', 'Wrangler'],['other models', 'Other Models'])],
['kia', 'Kia', new Array (['', 'All Models'],['amanti', 'Amanti'],['borrego', 'Borrego'],['forte', 'Forte'],['optima', 'Optima'],['rio', 'Rio'],['rondo', 'Rondo'],['sedona', 'Sedona'],['sephia', 'Sephia'],['sorento', 'Sorento'],['spectra', 'Spectra'],['sportage', 'Sportage'],['other models', 'Other Models'])],
['land rover', 'Land Rover', new Array (['', 'All Models'],['defender', 'Defender'],['discovery', 'Discovery'],['discovery series ii', 'Discovery Series II'],['freelander', 'Freelander'],['lr2', 'LR2'],['lr3', 'LR3'],['range rover', 'Range Rover'],['range rover sport', 'Range Rover Sport'],['other models', 'Other Models'])],
['lexus', 'Lexus', new Array (['', 'All Models'],['es 250', 'ES 250'],['es 300', 'ES 300'],['es 330', 'ES 330'],['es 350', 'ES 350'],['gs 300', 'GS 300'],['gs 350', 'GS 350'],['gs 400', 'GS 400'],['gs 430', 'GS 430'],['gs 450h', 'GS 450H'],['gs 460', 'GS 460'],['gx 470', 'GX 470'],['is 250', 'IS 250'],['is 300', 'IS 300'],['is 350', 'IS 350'],['is f', 'IS F'],['ls 400', 'LS 400'],['ls 430', 'LS 430'],['ls 460', 'LS 460'],['ls 600h l', 'LS 600h L'],['lx 450', 'LX 450'],['lx 470', 'LX 470'],['lx 570', 'LX 570'],['rx 300', 'RX 300'],['rx 330', 'RX 330'],['rx 350', 'RX 350'],['rx 400h', 'RX 400H'],['sc 300', 'SC 300'],['sc 400', 'SC 400'],['sc 430', 'SC 430'],['other models', 'Other Models'])],
['lincoln', 'Lincoln', new Array (['', 'All Models'],['aviator', 'Aviator'],['blackwood', 'Blackwood'],['continental', 'Continental'],['ls', 'LS'],['mark lt', 'Mark LT'],['mark vii', 'Mark VII'],['mark viii', 'Mark VIII'],['mks', 'MKS'],['mkx', 'MKX'],['mkz', 'MKZ'],['navigator', 'Navigator'],['navigator l', 'Navigator L'],['town car', 'Town Car'],['zephyr', 'Zephyr'],['other models', 'Other Models'])],
['mazda', 'Mazda', new Array (['', 'All Models'],['323', '323'],['626', '626'],['929', '929'],['b-series pickup', 'B-Series Pickup'],['b-series truck', 'B-Series Truck'],['cx-7', 'CX-7'],['cx-9', 'CX-9'],['mazda3', 'MAZDA3'],['mazda5', 'MAZDA5'],['mazda6', 'MAZDA6'],['mazdaspeed 3', 'MAZDASPEED 3'],['mazdaspeed 6', 'MAZDASPEED 6'],['mazdaspeed mx-5 miata', 'MAZDASPEED MX-5 Miata'],['mazdaspeed protege', 'MAZDASPEED Protege'],['millenia', 'Millenia'],['mpv', 'MPV'],['mx-3', 'MX-3'],['mx-5 miata', 'MX-5 Miata'],['mx-6', 'MX-6'],['navajo', 'Navajo'],['protege', 'Protege'],['protege5', 'Protege5'],['rx-7', 'RX-7'],['rx-8', 'RX-8'],['tribute', 'Tribute'],['tribute hybrid', 'Tribute Hybrid'],['truck', 'Truck'],['other models', 'Other Models'])],
['mercedes-benz', 'Mercedes-Benz', new Array (['', 'All Models'],['190-class', '190-Class'],['300-class', '300-Class'],['350-class', '350-Class'],['400-class', '400-Class'],['420-class', '420-Class'],['500-class', '500-Class'],['560-class', '560-Class'],['600-class', '600-Class'],['c-class', 'C-Class'],['c32 amg', 'C32 AMG'],['c36 amg', 'C36 AMG'],['c43 amg', 'C43 AMG'],['c55 amg', 'C55 AMG'],['cl-class', 'CL-Class'],['cl55 amg', 'CL55 AMG'],['cl65 amg', 'CL65 AMG'],['clk-class', 'CLK-Class'],['clk55 amg', 'CLK55 AMG'],['cls-class', 'CLS-Class'],['cls55 amg', 'CLS55 AMG'],['e-class', 'E-Class'],['e55 amg', 'E55 AMG'],['g-class', 'G-Class'],['g55 amg', 'G55 AMG'],['gl-class', 'GL-Class'],['m-class', 'M-Class'],['ml55 amg', 'ML55 AMG'],['r-class', 'R-Class'],['s-class', 'S-Class'],['s55 amg', 'S55 AMG'],['s65 amg', 'S65 AMG'],['sl-class', 'SL-Class'],['sl55 amg', 'SL55 AMG'],['sl65 amg', 'SL65 AMG'],['slk-class', 'SLK-Class'],['slk32 amg', 'SLK32 AMG'],['slk55 amg', 'SLK55 AMG'],['slr mclaren', 'SLR MCLAREN'],['other models', 'Other Models'])],
['mercury', 'Mercury', new Array (['', 'All Models'],['capri', 'Capri'],['cougar', 'Cougar'],['grand marquis', 'Grand Marquis'],['marauder', 'Marauder'],['mariner', 'Mariner'],['mariner hybrid', 'Mariner Hybrid'],['milan', 'Milan'],['montego', 'Montego'],['monterey', 'Monterey'],['mountaineer', 'Mountaineer'],['mystique', 'Mystique'],['sable', 'Sable'],['topaz', 'Topaz'],['tracer', 'Tracer'],['villager', 'Villager'],['other models', 'Other Models'])],
['mini', 'Mini', new Array (['', 'All Models'],['cooper', 'Cooper'],['other models', 'Other Models'])],
['mitsubishi', 'Mitsubishi', new Array (['', 'All Models'],['3000gt', '3000GT'],['diamante', 'Diamante'],['eclipse', 'Eclipse'],['eclipse spyder', 'Eclipse Spyder'],['endeavor', 'Endeavor'],['expo', 'Expo'],['galant', 'Galant'],['lancer', 'Lancer'],['lancer evolution', 'Lancer Evolution'],['lancer sportback', 'Lancer Sportback'],['mighty max pickup', 'Mighty Max Pickup'],['mirage', 'Mirage'],['montero', 'Montero'],['montero sport', 'Montero Sport'],['outlander', 'Outlander'],['precis', 'PreciS'],['raider', 'Raider'],['sigma', 'Sigma'],['vanwagon', 'Vanwagon'],['other models', 'Other Models'])],
['nissan', 'Nissan', new Array (['', 'All Models'],['200sx', '200SX'],['240sx', '240SX'],['300zx', '300ZX'],['350z', '350Z'],['altima', 'Altima'],['altima hybrid', 'Altima Hybrid'],['armada', 'Armada'],['axxess', 'Axxess'],['frontier', 'Frontier'],['gt-r', 'GT-R'],['maxima', 'Maxima'],['murano', 'Murano'],['nx', 'NX'],['pathfinder', 'Pathfinder'],['pulsar', 'Pulsar'],['quest', 'Quest'],['rogue', 'Rogue'],['sentra', 'Sentra'],['stanza', 'Stanza'],['titan', 'Titan'],['truck', 'Truck'],['van', 'Van'],['versa', 'Versa'],['xterra', 'Xterra'],['other models', 'Other Models'])],
['oldsmobile', 'Oldsmobile', new Array (['', 'All Models'],['achieva', 'Achieva'],['alero', 'Alero'],['aurora', 'Aurora'],['bravada', 'Bravada'],['ciera', 'Ciera'],['custom cruiser', 'Custom Cruiser'],['cutlass', 'Cutlass'],['cutlass calais', 'Cutlass Calais'],['cutlass ciera', 'Cutlass Ciera'],['cutlass supreme', 'Cutlass Supreme'],['eighty-eight', 'Eighty-Eight'],['eighty-eight royale', 'Eighty-Eight Royale'],['intrigue', 'Intrigue'],['lss', 'LSS'],['ninety-eight', 'Ninety-Eight'],['regency', 'Regency'],['silhouette', 'Silhouette'],['toronado', 'Toronado'],['other models', 'Other Models'])],
['plymouth', 'Plymouth', new Array (['', 'All Models'],['acclaim', 'Acclaim'],['breeze', 'Breeze'],['colt', 'Colt'],['grand voyager', 'Grand Voyager'],['horizon', 'Horizon'],['laser', 'Laser'],['neon', 'Neon'],['prowler', 'Prowler'],['sundance', 'Sundance'],['voyager', 'Voyager'],['other models', 'Other Models'])],
['pontiac', 'Pontiac', new Array (['', 'All Models'],['6000', '6000'],['aztek', 'Aztek'],['bonneville', 'Bonneville'],['firebird', 'Firebird'],['g5', 'G5'],['g6', 'G6'],['g8', 'G8'],['grand am', 'Grand Am'],['grand prix', 'Grand Prix'],['gto', 'GTO'],['le mans', 'Le Mans'],['montana', 'Montana'],['montana sv6', 'Montana SV6'],['solstice', 'Solstice'],['sunbird', 'Sunbird'],['sunfire', 'Sunfire'],['torrent', 'Torrent'],['trans sport', 'Trans Sport'],['vibe', 'Vibe'],['other models', 'Other Models'])],
['porsche', 'Porsche', new Array (['', 'All Models'],['911', '911'],['928', '928'],['944', '944'],['968', '968'],['boxster', 'Boxster'],['carrera gt', 'Carrera GT'],['cayenne', 'Cayenne'],['cayman', 'Cayman'],['cayman s', 'Cayman S'],['other models', 'Other Models'])],
['saab', 'Saab', new Array (['', 'All Models'],['9-2x', '9-2X'],['9-3', '9-3'],['9-5', '9-5'],['9-7x', '9-7X'],['900', '900'],['9000', '9000'],['other models', 'Other Models'])],
['saturn', 'Saturn', new Array (['', 'All Models'],['astra', 'Astra'],['aura', 'Aura'],['aura hybrid', 'Aura Hybrid'],['ion', 'Ion'],['ion red line', 'ION Red Line'],['l-series', 'L-Series'],['l300', 'L300'],['outlook', 'Outlook'],['relay', 'Relay'],['s-series', 'S-Series'],['sky', 'Sky'],['vue', 'Vue'],['vue hybrid', 'VUE Hybrid'],['other models', 'Other Models'])],
['scion', 'Scion', new Array (['', 'All Models'],['tc', 'tC'],['xa', 'xA'],['xb', 'xB'],['xd', 'xD'],['other models', 'Other Models'])],
['subaru', 'Subaru', new Array (['', 'All Models'],['b9 tribeca', 'B9 Tribeca'],['baja', 'Baja'],['forester', 'Forester'],['impreza', 'Impreza'],['impreza wrx sti', 'Impreza WRX STi'],['justy', 'Justy'],['legacy', 'Legacy'],['loyale', 'Loyale'],['outback', 'Outback'],['svx', 'SVX'],['tribeca', 'Tribeca'],['xt', 'XT'],['other models', 'Other Models'])],
['suzuki', 'Suzuki', new Array (['', 'All Models'],['aerio', 'Aerio'],['esteem', 'Esteem'],['forenza', 'Forenza'],['grand vitara', 'Grand Vitara'],['reno', 'Reno'],['samurai', 'Samurai'],['sidekick', 'Sidekick'],['swift', 'Swift'],['sx4', 'SX4'],['verona', 'Verona'],['vitara', 'Vitara'],['x-90', 'X-90'],['xl-7', 'XL-7'],['other models', 'Other Models'])],
['toyota', 'Toyota', new Array (['', 'All Models'],['4runner', '4Runner'],['avalon', 'Avalon'],['camry', 'Camry'],['camry hybrid', 'Camry Hybrid'],['camry solara', 'Camry Solara'],['celica', 'Celica'],['corolla', 'Corolla'],['cressida', 'Cressida'],['echo', 'Echo'],['fj cruiser', 'FJ Cruiser'],['highlander', 'Highlander'],['highlander hybrid', 'Highlander Hybrid'],['land cruiser', 'Land Cruiser'],['matrix', 'Matrix'],['mr2', 'MR2'],['mr2 spyder', 'MR2 Spyder'],['paseo', 'Paseo'],['pickup', 'Pickup'],['previa', 'Previa'],['prius', 'Prius'],['rav4', 'RAV4'],['sequoia', 'Sequoia'],['sienna', 'Sienna'],['supra', 'Supra'],['t100', 'T100'],['tacoma', 'Tacoma'],['tercel', 'Tercel'],['tundra', 'Tundra'],['yaris', 'Yaris'],['other models', 'Other Models'])],
['volkswagen', 'Volkswagen', new Array (['', 'All Models'],['cabrio', 'Cabrio'],['cabriolet', 'Cabriolet'],['cc', 'CC'],['corrado', 'Corrado'],['eos', 'Eos'],['eurovan', 'EUROVAN'],['fox', 'Fox'],['gli', 'GLI'],['golf', 'Golf'],['gti', 'GTI'],['jetta', 'Jetta'],['new beetle', 'New Beetle'],['passat', 'Passat'],['phaeton', 'Phaeton'],['r32', 'R32'],['rabbit', 'Rabbit'],['routan', 'Routan'],['tiguan', 'Tiguan'],['touareg', 'Touareg'],['touareg 2', 'Touareg 2'],['vanagon', 'Vanagon'],['other models', 'Other Models'])],
['volvo', 'Volvo', new Array (['', 'All Models'],['240', '240'],['740', '740'],['760', '760'],['780', '780'],['850', '850'],['940', '940'],['960', '960'],['c30', 'C30'],['c70', 'C70'],['coupe', 'Coupe'],['s40', 'S40'],['s60', 'S60'],['s60 r', 'S60 R'],['s70', 'S70'],['s80', 'S80'],['s90', 'S90'],['v40', 'V40'],['v50', 'V50'],['v70', 'V70'],['v70 r', 'V70 R'],['v90', 'V90'],['xc', 'XC'],['xc70', 'XC70'],['xc90', 'XC90'],['other models', 'Other Models'])],
['other makes', 'Other Makes', new Array (['', 'All Models'],['other models', 'Other Models'])]); 
 
for (var j = 0; j < makesData.length; j++) { 
  var makeDescriptor = new Object(); 
  makeDescriptor.id = makesData[j][0]; 
  // makeDescriptor.id = makesData[j][1];
  makeDescriptor.name = makesData[j][1]; 
  var modelsData = makesData[j][2]; 
  var allModels = new Array(); 
  
  
  for (var i = 0; i < modelsData.length; i++) { 
  	var model = new Object(); 
  	model.id = modelsData[i][0]; 
  	// model.id = modelsData[i][1]; 
  	model.displayName = modelsData[i][1]; 
  	allModels.push(model); 
  } 

  makeDescriptor.models = allModels; 
  allMakes[makeDescriptor.id] = makeDescriptor; 
} 

/* Nifty Corners Cube - rounded corners with CSS and Javascript
Copyright 2006 Alessandro Fulciniti (a.fulciniti@html.it)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

var niftyOk=(document.getElementById && document.createElement && Array.prototype.push);
var niftyCss=false;

String.prototype.find=function(what){
return(this.indexOf(what)>=0 ? true : false);
}

var oldonload=window.onload;
if(typeof(NiftyLoad)!='function') NiftyLoad=function(){};
if(typeof(oldonload)=='function')
    window.onload=function(){oldonload();AddCss();NiftyLoad()};
else window.onload=function(){AddCss();NiftyLoad()};

function AddCss(){
niftyCss=true;
var l=CreateEl("link");
l.setAttribute("type","text/css");
l.setAttribute("rel","stylesheet");
l.setAttribute("href",requestContextPath + "/css/niftyCorners.css");
l.setAttribute("media","screen");
document.getElementsByTagName("head")[0].appendChild(l);
}

function Nifty(selector,options){
if(niftyOk==false) return;
if(niftyCss==false) AddCss();
var i,v=selector.split(","),h=0;
if(options==null) options="";
if(options.find("fixed-height"))
    h=getElementsBySelector(v[0])[0].offsetHeight;
for(i=0;i<v.length;i++)
    Rounded(v[i],options);
if(options.find("height")) SameHeight(selector,h);
}

function Rounded(selector,options){
var i,top="",bottom="",v=new Array();
if(options!=""){
    options=options.replace("left","tl bl");
    options=options.replace("right","tr br");
    options=options.replace("top","tr tl");
    options=options.replace("bottom","br bl");
    options=options.replace("transparent","alias");
    if(options.find("tl")){
        top="both";
        if(!options.find("tr")) top="left";
        }
    else if(options.find("tr")) top="right";
    if(options.find("bl")){
        bottom="both";
        if(!options.find("br")) bottom="left";
        }
    else if(options.find("br")) bottom="right";
    }
if(top=="" && bottom=="" && !options.find("none")){top="both";bottom="both";}
v=getElementsBySelector(selector);
for(i=0;i<v.length;i++){
    FixIE(v[i]);
    if(top!="") AddTop(v[i],top,options);
    if(bottom!="") AddBottom(v[i],bottom,options);
    }
}

function AddTop(el,side,options){
var d=CreateEl("b"),lim=4,border="",p,i,btype="r",bk,color;
d.style.marginLeft="-"+getPadding(el,"Left")+"px";
d.style.marginRight="-"+getPadding(el,"Right")+"px";
if(options.find("alias") || (color=getBk(el))=="transparent"){
    color="transparent";bk="transparent"; border=getParentBk(el);btype="t";
    }
else{
    bk=getParentBk(el); border=Mix(color,bk);
    }
d.style.background=bk;
d.className="niftycorners";
p=getPadding(el,"Top");
if(options.find("small")){
    d.style.marginBottom=(p-2)+"px";
    btype+="s"; lim=2;
    }
else if(options.find("big")){
    d.style.marginBottom=(p-10)+"px";
    btype+="b"; lim=8;
    }
else d.style.marginBottom=(p-5)+"px";
for(i=1;i<=lim;i++)
    d.appendChild(CreateStrip(i,side,color,border,btype));
el.style.paddingTop="0";
el.insertBefore(d,el.firstChild);
}

function AddBottom(el,side,options){
var d=CreateEl("b"),lim=4,border="",p,i,btype="r",bk,color;
d.style.marginLeft="-"+getPadding(el,"Left")+"px";
d.style.marginRight="-"+getPadding(el,"Right")+"px";
if(options.find("alias") || (color=getBk(el))=="transparent"){
    color="transparent";bk="transparent"; border=getParentBk(el);btype="t";
    }
else{
    bk=getParentBk(el); border=Mix(color,bk);
    }
d.style.background=bk;
d.className="niftycorners";
p=getPadding(el,"Bottom");
if(options.find("small")){
    d.style.marginTop=(p-2)+"px";
    btype+="s"; lim=2;
    }
else if(options.find("big")){
    d.style.marginTop=(p-10)+"px";
    btype+="b"; lim=8;
    }
else d.style.marginTop=(p-5)+"px";
for(i=lim;i>0;i--)
    d.appendChild(CreateStrip(i,side,color,border,btype));
el.style.paddingBottom=0;
el.appendChild(d);
}

function CreateStrip(index,side,color,border,btype){
var x=CreateEl("b");
x.className=btype+index;
x.style.backgroundColor=color;
x.style.borderColor=border;
if(side=="left"){
    x.style.borderRightWidth="0";
    x.style.marginRight="0";
    }
else if(side=="right"){
    x.style.borderLeftWidth="0";
    x.style.marginLeft="0";
    }
return(x);
}

function CreateEl(x){
return(document.createElement(x));
}

function FixIE(el){
if(el.currentStyle!=null && el.currentStyle.hasLayout!=null && el.currentStyle.hasLayout==false)
    el.style.display="inline-block";
}

function SameHeight(selector,maxh){
var i,v=selector.split(","),t,j,els=[],gap;
for(i=0;i<v.length;i++){
    t=getElementsBySelector(v[i]);
    els=els.concat(t);
    }
for(i=0;i<els.length;i++){
    if(els[i].offsetHeight>maxh) maxh=els[i].offsetHeight;
    els[i].style.height="auto";
    }
for(i=0;i<els.length;i++){
    gap=maxh-els[i].offsetHeight;
    if(gap>0){
        t=CreateEl("b");t.className="niftyfill";t.style.height=gap+"px";
        nc=els[i].lastChild;
        if(nc.className=="niftycorners")
            els[i].insertBefore(t,nc);
        else els[i].appendChild(t);
        }
    }
}

function getElementsBySelector(selector){
var i,j,selid="",selclass="",tag=selector,tag2="",v2,k,f,a,s=[],objlist=[],c;
if(selector.find("#")){ //id selector like "tag#id"
    if(selector.find(" ")){  //descendant selector like "tag#id tag"
        s=selector.split(" ");
        var fs=s[0].split("#");
        if(fs.length==1) return(objlist);
        f=document.getElementById(fs[1]);
        if(f){
            v=f.getElementsByTagName(s[1]);
            for(i=0;i<v.length;i++) objlist.push(v[i]);
            }
        return(objlist);
        }
    else{
        s=selector.split("#");
        tag=s[0];
        selid=s[1];
        if(selid!=""){
            f=document.getElementById(selid);
            if(f) objlist.push(f);
            return(objlist);
            }
        }
    }
if(selector.find(".")){      //class selector like "tag.class"
    s=selector.split(".");
    tag=s[0];
    selclass=s[1];
    if(selclass.find(" ")){   //descendant selector like tag1.classname tag2
        s=selclass.split(" ");
        selclass=s[0];
        tag2=s[1];
        }
    }
var v=document.getElementsByTagName(tag);  // tag selector like "tag"
if(selclass==""){
    for(i=0;i<v.length;i++) objlist.push(v[i]);
    return(objlist);
    }
for(i=0;i<v.length;i++){
    c=v[i].className.split(" ");
    for(j=0;j<c.length;j++){
        if(c[j]==selclass){
            if(tag2=="") objlist.push(v[i]);
            else{
                v2=v[i].getElementsByTagName(tag2);
                for(k=0;k<v2.length;k++) objlist.push(v2[k]);
                }
            }
        }
    }
return(objlist);
}

function getParentBk(x){
var el=x.parentNode,c;
while(el.tagName.toUpperCase()!="HTML" && (c=getBk(el))=="transparent")
    el=el.parentNode;
if(c=="transparent") c="#FFFFFF";
return(c);
}

function getBk(x){
var c=getStyleProp(x,"backgroundColor");
if(c==null || c=="transparent" || c.find("rgba(0, 0, 0, 0)"))
    return("transparent");
if(c.find("rgb")) c=rgb2hex(c);
return(c);
}

function getPadding(x,side){
var p=getStyleProp(x,"padding"+side);
if(p==null || !p.find("px")) return(0);
return(parseInt(p));
}

function getStyleProp(x,prop){
if(x.currentStyle)
    return(x.currentStyle[prop]);
if(document.defaultView.getComputedStyle)
    return(document.defaultView.getComputedStyle(x,'')[prop]);
return(null);
}

function rgb2hex(value){
var hex="",v,h,i;
var regexp=/([0-9]+)[, ]+([0-9]+)[, ]+([0-9]+)/;
var h=regexp.exec(value);
for(i=1;i<4;i++){
    v=parseInt(h[i]).toString(16);
    if(v.length==1) hex+="0"+v;
    else hex+=v;
    }
return("#"+hex);
}

function Mix(c1,c2){
var i,step1,step2,x,y,r=new Array(3);
if(c1.length==4)step1=1;
else step1=2;
if(c2.length==4) step2=1;
else step2=2;
for(i=0;i<3;i++){
    x=parseInt(c1.substr(1+step1*i,step1),16);
    if(step1==1) x=16*x+x;
    y=parseInt(c2.substr(1+step2*i,step2),16);
    if(step2==1) y=16*y+y;
    r[i]=Math.floor((x*50+y*50)/100);
    r[i]=r[i].toString(16);
    if(r[i].length==1) r[i]="0"+r[i];
    }
return("#"+r[0]+r[1]+r[2]);
}

/* This notice must be untouched at all times.

wz_tooltip.js	 v. 4.12

The latest version is available at
http://www.walterzorn.com
or http://www.devira.com
or http://www.walterzorn.de

Copyright (c) 2002-2007 Walter Zorn. All rights reserved.
Created 1.12.2002 by Walter Zorn (Web: http://www.walterzorn.com )
Last modified: 13.7.2007

Easy-to-use cross-browser tooltips.
Just include the script at the beginning of the <body> section, and invoke
Tip('Tooltip text') from within the desired HTML onmouseover eventhandlers.
No container DIV, no onmouseouts required.
By default, width of tooltips is automatically adapted to content.
Is even capable of dynamically converting arbitrary HTML elements to tooltips
by calling TagToTip('ID_of_HTML_element_to_be_converted') instead of Tip(),
which means you can put important, search-engine-relevant stuff into tooltips.
Appearance of tooltips can be individually configured
via commands passed to Tip() or TagToTip().

Tab Width: 4
LICENSE: LGPL

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

For more details on the GNU Lesser General Public License,
see http://www.gnu.org/copyleft/lesser.html
*/

var config = new Object();


//===================  GLOBAL TOOPTIP CONFIGURATION  =========================//
var  tt_Debug	= true		// false or true - recommended: false once you release your page to the public
var  tt_Enabled	= true		// Allows to (temporarily) suppress tooltips, e.g. by providing the user with a button that sets this global variable to false
var  TagsToTip	= true		// false or true - if true, the script is capable of converting HTML elements to tooltips

// For each of the following config variables there exists a command, which is
// just the variablename in uppercase, to be passed to Tip() or TagToTip() to
// configure tooltips individually. Individual commands override global
// configuration. Order of commands is arbitrary.
// Example: onmouseover="Tip('Tooltip text', LEFT, true, BGCOLOR, '#FF9900', FADEIN, 400)"

config. Above			= false 	// false or true - tooltip above mousepointer?
config. BgColor 		= '#E4E7FF' // Background color
config. BgImg			= ''		// Path to background image, none if empty string ''
config. BorderColor 	= '#002299'
config. BorderStyle 	= 'solid'	// Any permitted CSS value, but I recommend 'solid', 'dotted' or 'dashed'
config. BorderWidth 	= 1
config. CenterMouse 	= false 	// false or true - center the tip horizontally below (or above) the mousepointer
config. ClickClose		= false 	// false or true - close tooltip if the user clicks somewhere
config. CloseBtn		= false 	// false or true - closebutton in titlebar
config. CloseBtnColors	= ['#990000', '#FFFFFF', '#DD3333', '#FFFFFF']	  // [Background, text, hovered background, hovered text] - use empty strings '' to inherit title colors
config. CloseBtnText	= '&nbsp;X&nbsp;'	// Close button text (may also be an image tag)
config. CopyContent		= true		// When converting a HTML element to a tooltip, copy only the element's content, rather than converting the element by its own
config. Delay			= 400		// Time span in ms until tooltip shows up
config. Duration		= 0 		// Time span in ms after which the tooltip disappears; 0 for infinite duration
config. FadeIn			= 0 		// Fade-in duration in ms, e.g. 400; 0 for no animation
config. FadeOut 		= 0
config. FadeInterval	= 30		// Duration of each fade step in ms (recommended: 30) - shorter is smoother but causes more CPU-load
config. Fix 			= null		// Fixated position - x- an y-oordinates in brackets, e.g. [210, 480], or null for no fixation
config. FollowMouse		= true		// false or true - tooltip follows the mouse
config. FontColor		= '#000044'
config. FontFace		= 'Verdana,Geneva,sans-serif'
config. FontSize		= '8pt' 	// E.g. '9pt' or '12px' - unit is mandatory
config. FontWeight		= 'normal'	// 'normal' or 'bold';
config. Left			= false 	// false or true - tooltip on the left of the mouse
config. OffsetX 		= 14		// Horizontal offset of left-top corner from mousepointer
config. OffsetY 		= 8 		// Vertical offset
config. Opacity 		= 100		// Integer between 0 and 100 - opacity of tooltip in percent
config. Padding 		= 3 		// Spacing between border and content
config. Shadow			= false 	// false or true
config. ShadowColor 	= '#C0C0C0'
config. ShadowWidth 	= 5
config. Sticky			= false 	// Do NOT hide tooltip on mouseout? false or true
config. TextAlign		= 'left'	// 'left', 'right' or 'justify'
config. Title			= ''		// Default title text applied to all tips (no default title: empty string '')
config. TitleAlign		= 'left'	// 'left' or 'right' - text alignment inside the title bar
config. TitleBgColor	= ''		// If empty string '', BorderColor will be used
config. TitleFontColor	= '#ffffff'	// Color of title text - if '', BgColor (of tooltip body) will be used
config. TitleFontFace	= ''		// If '' use FontFace (boldified)
config. TitleFontSize	= ''		// If '' use FontSize
config. Width			= 0 		// Tooltip width; 0 for automatic adaption to tooltip content
//=======  END OF TOOLTIP CONFIG, DO NOT CHANGE ANYTHING BELOW  ==============//




//======================  PUBLIC  ============================================//
function Tip()
{
	tt_Tip(arguments, null);
}
function TagToTip()
{
	if(TagsToTip)
	{
		var t2t = tt_GetElt(arguments[0]);
		if(t2t)
			tt_Tip(arguments, t2t);
	}
}

//==================  PUBLIC EXTENSION API	==================================//
// Extension eventhandlers currently supported:
// OnLoadConfig, OnCreateContentString, OnSubDivsCreated, OnShow, OnMoveBefore,
// OnMoveAfter, OnHideInit, OnHide, OnKill

var tt_aElt = new Array(10), // Container DIV, outer title & body DIVs, inner title & body TDs, closebutton SPAN, shadow DIVs, and IFRAME to cover windowed elements in IE
tt_aV = new Array(),	// Caches and enumerates config data for currently active tooltip
tt_sContent,			// Inner tooltip text or HTML
tt_scrlX = 0, tt_scrlY = 0,
tt_musX, tt_musY,
tt_over,
tt_x, tt_y, tt_w, tt_h; // Position, width and height of currently displayed tooltip

function tt_Extension()
{
	tt_ExtCmdEnum();
	tt_aExt[tt_aExt.length] = this;
	return this;
}
function tt_SetTipPos(x, y)
{
	var css = tt_aElt[0].style;

	tt_x = x;
	tt_y = y;
	css.left = x + "px";
	css.top = y + "px";
	if(tt_ie56)
	{
		var ifrm = tt_aElt[tt_aElt.length - 1];
		if(ifrm)
		{
			ifrm.style.left = css.left;
			ifrm.style.top = css.top;
		}
	}
}
function tt_Hide()
{
	if(tt_db && tt_iState)
	{
		if(tt_iState & 0x2)
		{
			tt_aElt[0].style.visibility = "hidden";
			tt_ExtCallFncs(0, "Hide");
		}
		tt_tShow.EndTimer();
		tt_tHide.EndTimer();
		tt_tDurt.EndTimer();
		tt_tFade.EndTimer();
		if(!tt_op && !tt_ie)
		{
			tt_tWaitMov.EndTimer();
			tt_bWait = false;
		}
		if(tt_aV[CLICKCLOSE])
			tt_RemEvtFnc(document, "mouseup", tt_HideInit);
		tt_AddRemOutFnc(false);
		tt_ExtCallFncs(0, "Kill");
		// In case of a TagToTip tooltip, hide converted DOM node and
		// re-insert it into document
		if(tt_t2t && !tt_aV[COPYCONTENT])
		{
			tt_t2t.style.display = "none";
			tt_MovDomNode(tt_t2t, tt_aElt[6], tt_t2tDad);
		}
		tt_iState = 0;
		tt_over = null;
		tt_ResetMainDiv();
		if(tt_aElt[tt_aElt.length - 1])
			tt_aElt[tt_aElt.length - 1].style.display = "none";
	}
}
function tt_GetElt(id)
{
	return(document.getElementById ? document.getElementById(id)
			: document.all ? document.all[id]
			: null);
}
function tt_GetDivW(el)
{
	return(el ? (el.offsetWidth || el.style.pixelWidth || 0) : 0);
}
function tt_GetDivH(el)
{
	return(el ? (el.offsetHeight || el.style.pixelHeight || 0) : 0);
}
function tt_GetScrollX()
{
	return(window.pageXOffset || (tt_db ? (tt_db.scrollLeft || 0) : 0));
}
function tt_GetScrollY()
{
	return(window.pageYOffset || (tt_db ? (tt_db.scrollTop || 0) : 0));
}
function tt_GetClientW()
{
	return(document.body && (typeof(document.body.clientWidth) != tt_u) ? document.body.clientWidth
			: (typeof(window.innerWidth) != tt_u) ? window.innerWidth
			: tt_db ? (tt_db.clientWidth || 0)
			: 0);
}
function tt_GetClientH()
{
	// Exactly this order seems to yield correct values in all major browsers
	return(document.body && (typeof(document.body.clientHeight) != tt_u) ? document.body.clientHeight
			: (typeof(window.innerHeight) != tt_u) ? window.innerHeight
			: tt_db ? (tt_db.clientHeight || 0)
			: 0);
}
function tt_GetEvtX(e)
{
	return (e ? ((typeof(e.pageX) != tt_u) ? e.pageX : (e.clientX + tt_scrlX)) : 0);
}
function tt_GetEvtY(e)
{
	return (e ? ((typeof(e.pageY) != tt_u) ? e.pageY : (e.clientY + tt_scrlY)) : 0);
}
function tt_AddEvtFnc(el, sEvt, PFnc)
{
	if(el)
	{
		if(el.addEventListener)
			el.addEventListener(sEvt, PFnc, false);
		else
			el.attachEvent("on" + sEvt, PFnc);
	}
}
function tt_RemEvtFnc(el, sEvt, PFnc)
{
	if(el)
	{
		if(el.removeEventListener)
			el.removeEventListener(sEvt, PFnc, false);
		else
			el.detachEvent("on" + sEvt, PFnc);
	}
}

//======================  PRIVATE  ===========================================//
var tt_aExt = new Array(),	// Array of extension objects

tt_db, tt_op, tt_ie, tt_ie56, tt_bBoxOld,	// Browser flags
tt_body,
tt_flagOpa, 			// Opacity support: 1=IE, 2=Khtml, 3=KHTML, 4=Moz, 5=W3C
tt_maxPosX, tt_maxPosY,
tt_iState = 0,			// Tooltip active |= 1, shown |= 2, move with mouse |= 4
tt_opa, 				// Currently applied opacity
tt_bJmpVert,			// Tip above mouse (or ABOVE tip below mouse)
tt_t2t, tt_t2tDad,		// Tag converted to tip, and its parent element in the document
tt_elDeHref,			// The tag from which Opera has removed the href attribute
// Timer
tt_tShow = new Number(0), tt_tHide = new Number(0), tt_tDurt = new Number(0),
tt_tFade = new Number(0), tt_tWaitMov = new Number(0),
tt_bWait = false,
tt_u = "undefined";


function tt_Init()
{
	tt_MkCmdEnum();
	// Send old browsers instantly to hell
	if(!tt_Browser() || !tt_MkMainDiv())
		return;
	tt_IsW3cBox();
	tt_OpaSupport();
	tt_AddEvtFnc(document, "mousemove", tt_Move);
	// In Debug mode we search for TagToTip() calls in order to notify
	// the user if they've forgotten to set the TagsToTip config flag
	if(TagsToTip || tt_Debug)
		tt_SetOnloadFnc();
	tt_AddEvtFnc(window, "scroll",
		function()
		{
			tt_scrlX = tt_GetScrollX();
			tt_scrlY = tt_GetScrollY();
			if(tt_iState && !(tt_aV[STICKY] && (tt_iState & 2)))
				tt_HideInit();
		} );
	// Ensure the tip be hidden when the page unloads
	tt_AddEvtFnc(window, "unload", tt_Hide);
	tt_Hide();
}
// Creates command names by translating config variable names to upper case
function tt_MkCmdEnum()
{
	var n = 0;
	for(var i in config)
		eval("window." + i.toString().toUpperCase() + " = " + n++);
	tt_aV.length = n;
}
function tt_Browser()
{
	var n, nv, n6, w3c;

	n = navigator.userAgent.toLowerCase(),
	nv = navigator.appVersion;
	tt_op = (document.defaultView && typeof(eval("w" + "indow" + "." + "o" + "p" + "er" + "a")) != tt_u);
	tt_ie = n.indexOf("msie") != -1 && document.all && !tt_op;
	if(tt_ie)
	{
		var ieOld = (!document.compatMode || document.compatMode == "BackCompat");
		tt_db = !ieOld ? document.documentElement : (document.body || null);
		if(tt_db)
			tt_ie56 = parseFloat(nv.substring(nv.indexOf("MSIE") + 5)) >= 5.5
					&& typeof document.body.style.maxHeight == tt_u;
	}
	else
	{
		tt_db = document.documentElement || document.body ||
				(document.getElementsByTagName ? document.getElementsByTagName("body")[0]
				: null);
		if(!tt_op)
		{
			n6 = document.defaultView && typeof document.defaultView.getComputedStyle != tt_u;
			w3c = !n6 && document.getElementById;
		}
	}
	tt_body = (document.getElementsByTagName ? document.getElementsByTagName("body")[0]
				: (document.body || null));
	if(tt_ie || n6 || tt_op || w3c)
	{
		if(tt_body && tt_db)
		{
			if(document.attachEvent || document.addEventListener)
				return true;
		}
		else
			tt_Err("wz_tooltip.js must be included INSIDE the body section,"
					+ " immediately after the opening <body> tag.");
	}
	tt_db = null;
	return false;
}
function tt_MkMainDiv()
{
	// Create the tooltip DIV
	if(tt_body.insertAdjacentHTML)
		tt_body.insertAdjacentHTML("afterBegin", tt_MkMainDivHtm());
	else if(typeof tt_body.innerHTML != tt_u && document.createElement && tt_body.appendChild)
		tt_body.appendChild(tt_MkMainDivDom());
	// FireFox Alzheimer bug
	if(window.tt_GetMainDivRefs && tt_GetMainDivRefs())
		return true;
	tt_db = null;
	return false;
}
function tt_MkMainDivHtm()
{
	return('<div id="WzTtDiV"></div>' +
			(tt_ie56 ? ('<iframe id="WzTtIfRm" src="javascript:false" scrolling="no" frameborder="0" style="filter:Alpha(opacity=0);position:absolute;top:0px;left:0px;display:none;"></iframe>')
			: ''));
}
function tt_MkMainDivDom()
{
	var el = document.createElement("div");
	if(el)
		el.id = "WzTtDiV";
	return el;
}
function tt_GetMainDivRefs()
{
	tt_aElt[0] = tt_GetElt("WzTtDiV");
	if(tt_ie56 && tt_aElt[0])
	{
		tt_aElt[tt_aElt.length - 1] = tt_GetElt("WzTtIfRm");
		if(!tt_aElt[tt_aElt.length - 1])
			tt_aElt[0] = null;
	}
	if(tt_aElt[0])
	{
		var css = tt_aElt[0].style;

		css.visibility = "hidden";
		css.position = "absolute";
		css.overflow = "hidden";
		return true;
	}
	return false;
}
function tt_ResetMainDiv()
{
	var w = (window.screen && screen.width) ? screen.width : 10000;

	tt_SetTipPos(-w, 0);
	tt_aElt[0].innerHTML = "";
	tt_aElt[0].style.width = (w - 1) + "px";
}
function tt_IsW3cBox()
{
	var css = tt_aElt[0].style;

	css.padding = "10px";
	css.width = "40px";
	tt_bBoxOld = (tt_GetDivW(tt_aElt[0]) == 40);
	css.padding = "0px";
	tt_ResetMainDiv();
}
function tt_OpaSupport()
{
	var css = tt_body.style;

	tt_flagOpa = (typeof(css.filter) != tt_u) ? 1
				: (typeof(css.KhtmlOpacity) != tt_u) ? 2
				: (typeof(css.KHTMLOpacity) != tt_u) ? 3
				: (typeof(css.MozOpacity) != tt_u) ? 4
				: (typeof(css.opacity) != tt_u) ? 5
				: 0;
}
// Ported from http://dean.edwards.name/weblog/2006/06/again/
// (Dean Edwards et al.)
function tt_SetOnloadFnc()
{
	tt_AddEvtFnc(document, "DOMContentLoaded", tt_HideSrcTags);
	tt_AddEvtFnc(window, "load", tt_HideSrcTags);
	if(tt_body.attachEvent)
		tt_body.attachEvent("onreadystatechange",
			function() {
				if(tt_body.readyState == "complete")
					tt_HideSrcTags();
			} );
	if(/WebKit|KHTML/i.test(navigator.userAgent))
	{
		var t = setInterval(function() {
					if(/loaded|complete/.test(document.readyState))
					{
						clearInterval(t);
						tt_HideSrcTags();
					}
				}, 10);
	}
}
function tt_HideSrcTags()
{
	if(!window.tt_HideSrcTags || window.tt_HideSrcTags.done)
		return;
	window.tt_HideSrcTags.done = true;
	if(!tt_HideSrcTagsRecurs(tt_body))
		tt_Err("To enable the capability to convert HTML elements to tooltips,"
				+ " you must set TagsToTip in the global tooltip configuration"
				+ " to true.");
}
function tt_HideSrcTagsRecurs(dad)
{
	var a, ovr, asT2t;

	// Walk the DOM tree for tags that have an onmouseover attribute
	// containing a TagToTip('...') call.
	// (.childNodes first since .children is bugous in Safari)
	a = dad.childNodes || dad.children || null;
	for(var i = a ? a.length : 0; i;)
	{--i;
		if(!tt_HideSrcTagsRecurs(a[i]))
			return false;
		ovr = a[i].getAttribute ? a[i].getAttribute("onmouseover")
				: (typeof a[i].onmouseover == "function") ? a[i].onmouseover
				: null;
		if(ovr)
		{
			asT2t = ovr.toString().match(/TagToTip\s*\(\s*'[^'.]+'\s*[\),]/);
			if(asT2t && asT2t.length)
			{
				if(!tt_HideSrcTag(asT2t[0]))
					return false;
			}
		}
	}
	return true;
}
function tt_HideSrcTag(sT2t)
{
	var id, el;

	// The ID passed to the found TagToTip() call identifies an HTML element
	// to be converted to a tooltip, so hide that element
	id = sT2t.replace(/.+'([^'.]+)'.+/, "$1");
	el = tt_GetElt(id);
	if(el)
	{
		if(tt_Debug && !TagsToTip)
			return false;
		else
			el.style.display = "none";
	}
	else
		tt_Err("Invalid ID\n'" + id + "'\npassed to TagToTip()."
				+ " There exists no HTML element with that ID.");
	return true;
}
function tt_Tip(arg, t2t)
{
	if(!tt_db)
		return;
	if(tt_iState)
		tt_Hide();
	if(!tt_Enabled)
		return;
	tt_t2t = t2t;
	if(!tt_ReadCmds(arg))
		return;
	tt_iState = 0x1 | 0x4;
	tt_AdaptConfig1();
	tt_MkTipContent(arg);
	tt_MkTipSubDivs();
	tt_FormatTip();
	tt_bJmpVert = false;
	tt_maxPosX = tt_GetClientW() + tt_scrlX - tt_w - 1;
	tt_maxPosY = tt_GetClientH() + tt_scrlY - tt_h - 1;
	tt_AdaptConfig2();
	// We must fake the first mousemove in order to ensure the tip
	// be immediately shown and positioned
	tt_Move();
	tt_ShowInit();
}
function tt_ReadCmds(a)
{
	var i;

	// First load the global config values, to initialize also values
	// for which no command has been passed
	i = 0;
	for(var j in config)
		tt_aV[i++] = config[j];
	// Then replace each cached config value for which a command has been
	// passed (ensure the # of command args plus value args be even)
	if(a.length & 1)
	{
		for(i = a.length - 1; i > 0; i -= 2)
			tt_aV[a[i - 1]] = a[i];
		return true;
	}
	tt_Err("Incorrect call of Tip() or TagToTip().\n"
			+ "Each command must be followed by a value.");
	return false;
}
function tt_AdaptConfig1()
{
	tt_ExtCallFncs(0, "LoadConfig");
	// Inherit unspecified title formattings from body
	if(!tt_aV[TITLEBGCOLOR].length)
		tt_aV[TITLEBGCOLOR] = tt_aV[BORDERCOLOR];
	if(!tt_aV[TITLEFONTCOLOR].length)
		tt_aV[TITLEFONTCOLOR] = tt_aV[BGCOLOR];
	if(!tt_aV[TITLEFONTFACE].length)
		tt_aV[TITLEFONTFACE] = tt_aV[FONTFACE];
	if(!tt_aV[TITLEFONTSIZE].length)
		tt_aV[TITLEFONTSIZE] = tt_aV[FONTSIZE];
	if(tt_aV[CLOSEBTN])
	{
		// Use title colors for non-specified closebutton colors
		if(!tt_aV[CLOSEBTNCOLORS])
			tt_aV[CLOSEBTNCOLORS] = new Array("", "", "", "");
		for(var i = 4; i;)
		{--i;
			if(!tt_aV[CLOSEBTNCOLORS][i].length)
				tt_aV[CLOSEBTNCOLORS][i] = (i & 1) ? tt_aV[TITLEFONTCOLOR] : tt_aV[TITLEBGCOLOR];
		}
		// Enforce titlebar be shown
		if(!tt_aV[TITLE].length)
			tt_aV[TITLE] = " ";
	}
	// Circumvents broken display of images and fade-in flicker in Geckos < 1.8
	if(tt_aV[OPACITY] == 100 && typeof tt_aElt[0].style.MozOpacity != tt_u && !Array.every)
		tt_aV[OPACITY] = 99;
	// Smartly shorten the delay for fade-in tooltips
	if(tt_aV[FADEIN] && tt_flagOpa && tt_aV[DELAY] > 100)
		tt_aV[DELAY] = Math.max(tt_aV[DELAY] - tt_aV[FADEIN], 100);
}
function tt_AdaptConfig2()
{
	if(tt_aV[CENTERMOUSE])
		tt_aV[OFFSETX] -= ((tt_w - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0)) >> 1);
}
// Expose content globally so extensions can modify it
function tt_MkTipContent(a)
{
	if(tt_t2t)
	{
		if(tt_aV[COPYCONTENT])
			tt_sContent = tt_t2t.innerHTML;
		else
			tt_sContent = "";
	}
	else
		tt_sContent = a[0];
	tt_ExtCallFncs(0, "CreateContentString");
}
function tt_MkTipSubDivs()
{
	var sCss = 'position:relative;margin:0px;padding:0px;border-width:0px;left:0px;top:0px;line-height:normal;width:auto;',
	sTbTrTd = ' cellspacing=0 cellpadding=0 border=0 style="' + sCss + '"><tbody style="' + sCss + '"><tr><td ';

	tt_aElt[0].innerHTML =
		(''
		+ (tt_aV[TITLE].length ?
			('<div id="WzTiTl" style="position:relative;z-index:1;">'
			+ '<table id="WzTiTlTb"' + sTbTrTd + 'id="WzTiTlI" style="' + sCss + '">'
			+ tt_aV[TITLE]
			+ '</td>'
			+ (tt_aV[CLOSEBTN] ?
				('<td align="right" style="' + sCss
				+ 'text-align:right;">'
				+ '<span id="WzClOsE" style="padding-left:2px;padding-right:2px;'
				+ 'cursor:' + (tt_ie ? 'hand' : 'pointer')
				+ ';" onmouseover="tt_OnCloseBtnOver(1)" onmouseout="tt_OnCloseBtnOver(0)" onclick="tt_HideInit()">'
				+ tt_aV[CLOSEBTNTEXT]
				+ '</span></td>')
				: '')
			+ '</tr></tbody></table></div>')
			: '')
		+ '<div id="WzBoDy" style="position:relative;z-index:0;">'
		+ '<table' + sTbTrTd + 'id="WzBoDyI" style="' + sCss + '">'
		+ tt_sContent
		+ '</td></tr></tbody></table></div>'
		+ (tt_aV[SHADOW]
			? ('<div id="WzTtShDwR" style="position:absolute;overflow:hidden;"></div>'
				+ '<div id="WzTtShDwB" style="position:relative;overflow:hidden;"></div>')
			: '')
		);
	tt_GetSubDivRefs();
	// Convert DOM node to tip
	if(tt_t2t && !tt_aV[COPYCONTENT])
	{
		// Store the tag's parent element so we can restore that DOM branch
		// once the tooltip is hidden
		tt_t2tDad = tt_t2t.parentNode || tt_t2t.parentElement || tt_t2t.offsetParent || null;
		if(tt_t2tDad)
		{
			tt_MovDomNode(tt_t2t, tt_t2tDad, tt_aElt[6]);
			tt_t2t.style.display = "block";
		}
	}
	tt_ExtCallFncs(0, "SubDivsCreated");
}
function tt_GetSubDivRefs()
{
	var aId = new Array("WzTiTl", "WzTiTlTb", "WzTiTlI", "WzClOsE", "WzBoDy", "WzBoDyI", "WzTtShDwB", "WzTtShDwR");

	for(var i = aId.length; i; --i)
		tt_aElt[i] = tt_GetElt(aId[i - 1]);
}
function tt_FormatTip()
{
	var css, w, iOffY, iOffSh;

	//--------- Title DIV ----------
	if(tt_aV[TITLE].length)
	{
		css = tt_aElt[1].style;
		css.background = tt_aV[TITLEBGCOLOR];
		css.paddingTop = (tt_aV[CLOSEBTN] ? 2 : 0) + "px";
		css.paddingBottom = "1px";
		css.paddingLeft = css.paddingRight = tt_aV[PADDING] + "px";
		css = tt_aElt[3].style;
		css.color = tt_aV[TITLEFONTCOLOR];
		css.fontFamily = tt_aV[TITLEFONTFACE];
		css.fontSize = tt_aV[TITLEFONTSIZE];
		css.fontWeight = "bold";
		css.textAlign = tt_aV[TITLEALIGN];
		// Close button DIV
		if(tt_aElt[4])
		{
			css.paddingRight = (tt_aV[PADDING] << 1) + "px";
			css = tt_aElt[4].style;
			css.background = tt_aV[CLOSEBTNCOLORS][0];
			css.color = tt_aV[CLOSEBTNCOLORS][1];
			css.fontFamily = tt_aV[TITLEFONTFACE];
			css.fontSize = tt_aV[TITLEFONTSIZE];
			css.fontWeight = "bold";
		}
		if(tt_aV[WIDTH] > 0)
			tt_w = tt_aV[WIDTH] + ((tt_aV[PADDING] + tt_aV[BORDERWIDTH]) << 1);
		else
		{
			tt_w = tt_GetDivW(tt_aElt[3]) + tt_GetDivW(tt_aElt[4]);
			// Some spacing between title DIV and closebutton
			if(tt_aElt[4])
				tt_w += tt_aV[PADDING];
		}
		// Ensure the top border of the body DIV be covered by the title DIV
		iOffY = -tt_aV[BORDERWIDTH];
	}
	else
	{
		tt_w = 0;
		iOffY = 0;
	}

	//-------- Body DIV ------------
	css = tt_aElt[5].style;
	css.top = iOffY + "px";
	if(tt_aV[BORDERWIDTH])
	{
		css.borderColor = tt_aV[BORDERCOLOR];
		css.borderStyle = tt_aV[BORDERSTYLE];
		css.borderWidth = tt_aV[BORDERWIDTH] + "px";
	}
	if(tt_aV[BGCOLOR].length)
		css.background = tt_aV[BGCOLOR];
	if(tt_aV[BGIMG].length)
		css.backgroundImage = "url(" + tt_aV[BGIMG] + ")";
	css.padding = tt_aV[PADDING] + "px";
	css.textAlign = tt_aV[TEXTALIGN];
	// TD inside body DIV
	css = tt_aElt[6].style;
	css.color = tt_aV[FONTCOLOR];
	css.fontFamily = tt_aV[FONTFACE];
	css.fontSize = tt_aV[FONTSIZE];
	css.fontWeight = tt_aV[FONTWEIGHT];
	css.background = "";
	css.textAlign = tt_aV[TEXTALIGN];
	if(tt_aV[WIDTH] > 0)
		w = tt_aV[WIDTH] + ((tt_aV[PADDING] + tt_aV[BORDERWIDTH]) << 1);
	else
		// We measure the width of the body's inner TD, because some browsers
		// expand the width of the container and outer body DIV to 100%
		w = tt_GetDivW(tt_aElt[6]) + ((tt_aV[PADDING] + tt_aV[BORDERWIDTH]) << 1);
	if(w > tt_w)
		tt_w = w;

	//--------- Shadow DIVs ------------
	if(tt_aV[SHADOW])
	{
		tt_w += tt_aV[SHADOWWIDTH];
		iOffSh = Math.floor((tt_aV[SHADOWWIDTH] * 4) / 3);
		// Bottom shadow
		css = tt_aElt[7].style;
		css.top = iOffY + "px";
		css.left = iOffSh + "px";
		css.width = (tt_w - iOffSh - tt_aV[SHADOWWIDTH]) + "px";
		css.height = tt_aV[SHADOWWIDTH] + "px";
		css.background = tt_aV[SHADOWCOLOR];
		// Right shadow
		css = tt_aElt[8].style;
		css.top = iOffSh + "px";
		css.left = (tt_w - tt_aV[SHADOWWIDTH]) + "px";
		css.width = tt_aV[SHADOWWIDTH] + "px";
		css.background = tt_aV[SHADOWCOLOR];
	}
	else
		iOffSh = 0;

	//-------- Container DIV -------
	tt_SetTipOpa(tt_aV[FADEIN] ? 0 : tt_aV[OPACITY]);
	tt_FixSize(iOffY, iOffSh);
}
// Fixate the size so it can't dynamically change while the tooltip is moving.
function tt_FixSize(iOffY, iOffSh)
{
	var wIn, wOut, i;

	tt_aElt[0].style.width = tt_w + "px";
	tt_aElt[0].style.pixelWidth = tt_w;
	wOut = tt_w - ((tt_aV[SHADOW]) ? tt_aV[SHADOWWIDTH] : 0);
	// Body
	wIn = wOut;
	if(!tt_bBoxOld)
		wIn -= ((tt_aV[PADDING] + tt_aV[BORDERWIDTH]) << 1);
	tt_aElt[5].style.width = wIn + "px";
	// Title
	if(tt_aElt[1])
	{
		wIn = wOut - (tt_aV[PADDING] << 1);
		if(!tt_bBoxOld)
			wOut = wIn;
		tt_aElt[1].style.width = wOut + "px";
		tt_aElt[2].style.width = wIn + "px";
	}
	tt_h = tt_GetDivH(tt_aElt[0]) + iOffY;
	// Right shadow
	if(tt_aElt[8])
		tt_aElt[8].style.height = (tt_h - iOffSh) + "px";
	i = tt_aElt.length - 1;
	if(tt_aElt[i])
	{
		tt_aElt[i].style.width = tt_w + "px";
		tt_aElt[i].style.height = tt_h + "px";
	}
}
function tt_DeAlt(el)
{
	var aKid;

	if(el.alt)
		el.alt = "";
	if(el.title)
		el.title = "";
	aKid = el.childNodes || el.children || null;
	if(aKid)
	{
		for(var i = aKid.length; i;)
			tt_DeAlt(aKid[--i]);
	}
}
// This hack removes the annoying native tooltips over links in Opera
function tt_OpDeHref(el)
{
	if(!tt_op)
		return;
	if(tt_elDeHref)
		tt_OpReHref();
	while(el)
	{
		if(el.hasAttribute("href"))
		{
			el.t_href = el.getAttribute("href");
			el.t_stats = window.status;
			el.removeAttribute("href");
			el.style.cursor = "hand";
			tt_AddEvtFnc(el, "mousedown", tt_OpReHref);
			window.status = el.t_href;
			tt_elDeHref = el;
			break;
		}
		el = el.parentElement;
	}
}
function tt_ShowInit()
{
	tt_tShow.Timer("tt_Show()", tt_aV[DELAY], true);
	if(tt_aV[CLICKCLOSE])
		tt_AddEvtFnc(document, "mouseup", tt_HideInit);
}
function tt_OverInit(e)
{
	tt_over = e.target || e.srcElement;
	tt_DeAlt(tt_over);
	tt_OpDeHref(tt_over);
	tt_AddRemOutFnc(true);
}
function tt_Show()
{
	var css = tt_aElt[0].style;

	// Override the z-index of the topmost wz_dragdrop.js D&D item
	css.zIndex = Math.max((window.dd && dd.z) ? (dd.z + 2) : 0, 1010);
	if(tt_aV[STICKY] || !tt_aV[FOLLOWMOUSE])
		tt_iState &= ~0x4;
	if(tt_aV[DURATION] > 0)
		tt_tDurt.Timer("tt_HideInit()", tt_aV[DURATION], true);
	tt_ExtCallFncs(0, "Show")
	css.visibility = "visible";
	tt_iState |= 0x2;
	if(tt_aV[FADEIN])
		tt_Fade(0, 0, tt_aV[OPACITY], Math.round(tt_aV[FADEIN] / tt_aV[FADEINTERVAL]));
	tt_ShowIfrm();
}
function tt_ShowIfrm()
{
	if(tt_ie56)
	{
		var ifrm = tt_aElt[tt_aElt.length - 1];
		if(ifrm)
		{
			var css = ifrm.style;
			css.zIndex = tt_aElt[0].style.zIndex - 1;
			css.display = "block";
		}
	}
}
function tt_Move(e)
{
	e = window.event || e;
	if(e)
	{
		tt_musX = tt_GetEvtX(e);
		tt_musY = tt_GetEvtY(e);
	}
	if(tt_iState)
	{
		if(!tt_over && e)
			tt_OverInit(e);
		if(tt_iState & 0x4)
		{
			// Protect some browsers against jam of mousemove events
			if(!tt_op && !tt_ie)
			{
				if(tt_bWait)
					return;
				tt_bWait = true;
				tt_tWaitMov.Timer("tt_bWait = false;", 1, true);
			}
			if(tt_aV[FIX])
			{
				tt_iState &= ~0x4;
				tt_SetTipPos(tt_aV[FIX][0], tt_aV[FIX][1]);
			}
			else if(!tt_ExtCallFncs(e, "MoveBefore"))
				tt_SetTipPos(tt_PosX(), tt_PosY());
			tt_ExtCallFncs([tt_musX, tt_musY], "MoveAfter")
		}
	}
}
function tt_PosX()
{
	var x;

	x = tt_musX;
	if(tt_aV[LEFT])
		x -= tt_w + tt_aV[OFFSETX] - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0);
	else
		x += tt_aV[OFFSETX];
	// Prevent tip from extending past right/left clientarea boundary
	if(x > tt_maxPosX)
		x = tt_maxPosX;
	return((x < tt_scrlX) ? tt_scrlX : x);
}
function tt_PosY()
{
	var y;

	// Apply some hysteresis after the tip has snapped to the other side of the
	// mouse. In case of insufficient space above and below the mouse, we place
	// the tip below.
	if(tt_aV[ABOVE] && (!tt_bJmpVert || tt_CalcPosYAbove() >= tt_scrlY + 16))
		y = tt_DoPosYAbove();
	else if(!tt_aV[ABOVE] && tt_bJmpVert && tt_CalcPosYBelow() > tt_maxPosY - 16)
		y = tt_DoPosYAbove();
	else
		y = tt_DoPosYBelow();
	// Snap to other side of mouse if tip would extend past window boundary
	if(y > tt_maxPosY)
		y = tt_DoPosYAbove();
	if(y < tt_scrlY)
		y = tt_DoPosYBelow();
	return y;
}
function tt_DoPosYBelow()
{
	tt_bJmpVert = tt_aV[ABOVE];
	return tt_CalcPosYBelow();
}
function tt_DoPosYAbove()
{
	tt_bJmpVert = !tt_aV[ABOVE];
	return tt_CalcPosYAbove();
}
function tt_CalcPosYBelow()
{
	return(tt_musY + tt_aV[OFFSETY]);
}
function tt_CalcPosYAbove()
{
	var dy = tt_aV[OFFSETY] - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0);
	if(tt_aV[OFFSETY] > 0 && dy <= 0)
		dy = 1;
	return(tt_musY - tt_h - dy);
}
function tt_OnOut()
{
	tt_AddRemOutFnc(false);
	if(!(tt_aV[STICKY] && (tt_iState & 0x2)))
		tt_HideInit();
}
function tt_HideInit()
{
	tt_ExtCallFncs(0, "HideInit");
	tt_iState &= ~0x4;
	if(tt_flagOpa && tt_aV[FADEOUT])
	{
		tt_tFade.EndTimer();
		if(tt_opa)
		{
			var n = Math.round(tt_aV[FADEOUT] / (tt_aV[FADEINTERVAL] * (tt_aV[OPACITY] / tt_opa)));
			tt_Fade(tt_opa, tt_opa, 0, n);
			return;
		}
	}
	tt_tHide.Timer("tt_Hide();", 1, false);
}
function tt_OpReHref()
{
	if(tt_elDeHref)
	{
		tt_elDeHref.setAttribute("href", tt_elDeHref.t_href);
		tt_RemEvtFnc(tt_elDeHref, "mousedown", tt_OpReHref);
		window.status = tt_elDeHref.t_stats;
		tt_elDeHref = null;
	}
}
function tt_Fade(a, now, z, n)
{
	if(n)
	{
		now += Math.round((z - now) / n);
		if((z > a) ? (now >= z) : (now <= z))
			now = z;
		else
			tt_tFade.Timer("tt_Fade("
							+ a + "," + now + "," + z + "," + (n - 1)
							+ ")",
							tt_aV[FADEINTERVAL],
							true);
	}
	now ? tt_SetTipOpa(now) : tt_Hide();
}
// To circumvent the opacity nesting flaws of IE, we set the opacity
// for each sub-DIV separately, rather than for the container DIV.
function tt_SetTipOpa(opa)
{
	tt_SetOpa(tt_aElt[5].style, opa);
	if(tt_aElt[1])
		tt_SetOpa(tt_aElt[1].style, opa);
	if(tt_aV[SHADOW])
	{
		opa = Math.round(opa * 0.8);
		tt_SetOpa(tt_aElt[7].style, opa);
		tt_SetOpa(tt_aElt[8].style, opa);
	}
}
function tt_OnCloseBtnOver(iOver)
{
	var css = tt_aElt[4].style;

	iOver <<= 1;
	css.background = tt_aV[CLOSEBTNCOLORS][iOver];
	css.color = tt_aV[CLOSEBTNCOLORS][iOver + 1];
}
function tt_Int(x)
{
	var y;

	return(isNaN(y = parseInt(x)) ? 0 : y);
}
// Adds or removes the document.mousemove or HoveredElem.mouseout handler
// conveniently. Keeps track of those handlers to prevent them from being
// set or removed redundantly.
function tt_AddRemOutFnc(bAdd)
{
	var PSet = bAdd ? tt_AddEvtFnc : tt_RemEvtFnc;

	if(bAdd != tt_AddRemOutFnc.bOn)
	{
		PSet(tt_over, "mouseout", tt_OnOut);
		tt_AddRemOutFnc.bOn = bAdd;
		if(!bAdd)
			tt_OpReHref();
	}
}
tt_AddRemOutFnc.bOn = false;
Number.prototype.Timer = function(s, iT, bUrge)
{
	if(!this.value || bUrge)
		this.value = window.setTimeout(s, iT);
}
Number.prototype.EndTimer = function()
{
	if(this.value)
	{
		window.clearTimeout(this.value);
		this.value = 0;
	}
}
function tt_SetOpa(css, opa)
{
	tt_opa = opa;
	if(tt_flagOpa == 1)
	{
		// Hack for bugs of IE:
		// A DIV cannot be made visible in a single step if an opacity < 100
		// has been applied while the DIV was hidden.
		// Moreover, in IE6, applying an opacity < 100 has no effect if the
		// concerned element has no layout (position, size, zoom, ...).
		if(opa < 100)
		{
			var bVis = css.visibility != "hidden";
			css.zoom = "100%";
			if(!bVis)
				css.visibility = "visible";
			css.filter = "alpha(opacity=" + opa + ")";
			if(!bVis)
				css.visibility = "hidden";
		}
		else
			css.filter = "";
	}
	else
	{
		opa /= 100.0;
		switch(tt_flagOpa)
		{
		case 2:
			css.KhtmlOpacity = opa; break;
		case 3:
			css.KHTMLOpacity = opa; break;
		case 4:
			css.MozOpacity = opa; break;
		case 5:
			css.opacity = opa; break;
		}
	}
}
function tt_MovDomNode(el, dadFrom, dadTo)
{
	if(dadFrom)
		dadFrom.removeChild(el);
	if(dadTo)
		dadTo.appendChild(el);
}
function tt_Err(sErr)
{
	if(tt_Debug)
		alert("Tooltip Script Error Message:\n\n" + sErr);
}

//===========  DEALING WITH EXTENSIONS	==============//
function tt_ExtCmdEnum()
{
	var s;

	// Add new command(s) to the commands enum
	for(var i in config)
	{
		s = "window." + i.toString().toUpperCase();
		if(eval("typeof(" + s + ") == tt_u"))
		{
			eval(s + " = " + tt_aV.length);
			tt_aV[tt_aV.length] = null;
		}
	}
}
function tt_ExtCallFncs(arg, sFnc)
{
	var b = false;
	for(var i = tt_aExt.length; i;)
	{--i;
		var fnc = tt_aExt[i]["On" + sFnc];
		// Call the method the extension has defined for this event
		if(fnc && fnc(arg))
			b = true;
	}
	return b;
}

tt_Init();

config. BalloonImgPath = "/images/tip_balloon/";
/*
tip_balloon.js  v. 1.2

The latest version is available at
http://www.walterzorn.com
or http://www.devira.com
or http://www.walterzorn.de

Initial author: Walter Zorn
Last modified: 13.7.2007

Extension for the tooltip library wz_tooltip.js.
Implements balloon tooltips.
*/

// Here we define new global configuration variable(s) (as members of the
// predefined "config." class).
// From each of these config variables, wz_tooltip.js will automatically derive
// a command which can be passed to Tip() or TagToTip() in order to customize
// tooltips individually. These command names are just the config variable
// name(s) translated to uppercase,
// e.g. from config. Balloon a command BALLOON will automatically be
// created.

//===================  GLOBAL TOOPTIP CONFIGURATION  =========================//
config. Balloon = false				// true or false - set to true if you want this to be the default behaviour
//config. BalloonImgPath = "/images/tip_balloon/" // Path to images (border, corners, stem), in quotes. Path must be relative to your HTML file.
// Sizes of balloon images
config. BalloonEdgeSize = 5			// Integer - sidelength of quadratic corner images
config. BalloonStemWidth = 15		// Integer
config. BalloonStemHeight = 19		// Integer
//=======  END OF TOOLTIP CONFIG, DO NOT CHANGE ANYTHING BELOW  ==============//


// Create a new tt_Extension object (make sure that the name of that object,
// here balloon, is unique amongst the extensions available for wz_tooltips.js):
var balloon = new tt_Extension();

// Implement extension eventhandlers on which our extension should react

balloon.OnLoadConfig = function()
{
	if(tt_aV[BALLOON])
	{
		// Turn off native style properties which are not appropriate
		balloon.padding = Math.max(tt_aV[PADDING] - tt_aV[BALLOONEDGESIZE], 0);
		balloon.width = tt_aV[WIDTH];
		//if(tt_bBoxOld)
		//	balloon.width += (balloon.padding << 1);
		tt_aV[BORDERWIDTH] = 0;
		tt_aV[WIDTH] = 0;
		tt_aV[PADDING] = 0;
		tt_aV[BGCOLOR] = "";
		tt_aV[BGIMG] = "";
		tt_aV[SHADOW] = false;
		// Append slash to img path if missing
		if(tt_aV[BALLOONIMGPATH].charAt(tt_aV[BALLOONIMGPATH].length - 1) != '/')
			tt_aV[BALLOONIMGPATH] += "/";
		return true;
	}
	return false;
};
balloon.OnCreateContentString = function()
{
	if(!tt_aV[BALLOON])
		return false;
		
	var aImg, sImgZ, sCssCrn, sCssImg;

	// Cache balloon images in advance:
	// Either use the pre-cached default images...
	if(tt_aV[BALLOONIMGPATH] == config.BalloonImgPath)
		aImg = balloon.aDefImg;
	// ...or load images from different directory
	else
		aImg = Balloon_CacheImgs(tt_aV[BALLOONIMGPATH]);
	sCssCrn = ' style="position:relative;width:' + tt_aV[BALLOONEDGESIZE] + 'px;padding:0px;margin:0px;overflow:hidden;line-height:0px;"';
	sCssImg = 'padding:0px;margin:0px;border:0px;';
	sImgZ = '" style="' + sCssImg + '" />';
	
	tt_sContent = '<table border="0" cellpadding="0" cellspacing="0" style="width:auto;padding:0px;margin:0px;left:0px;top:0px;"><tr>'
				// Left-top corner
				+ '<td' + sCssCrn + ' valign="bottom">'
				+ '<img src="' + aImg[1].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
				+ '</td>'
				// Top border
				+ '<td valign="bottom" style="position:relative;padding:0px;margin:0px;overflow:hidden;">'
				+ '<img id="bALlOOnT" style="position:relative;top:1px;z-index:1;display:none;' + sCssImg + '" src="' + aImg[9].src + '" width="' + tt_aV[BALLOONSTEMWIDTH] + '" height="' + tt_aV[BALLOONSTEMHEIGHT] + '" />'
				+ '<div style="position:relative;z-index:0;padding:0px;margin:0px;overflow:hidden;width:auto;height:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[2].src + ');">'
				+ '</div>'
				+ '</td>'
				// Right-top corner
				+ '<td' + sCssCrn + ' valign="bottom">'
				+ '<img src="' + aImg[3].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
				+ '</td>'
				+ '</tr><tr>'
				// Left border
				+ '<td style="position:relative;padding:0px;margin:0px;width:' + tt_aV[BALLOONEDGESIZE] + 'px;overflow:hidden;background-image:url(' + aImg[8].src + ');">'
				// Redundant image for bugous old Geckos that won't auto-expand TD height to 100%
				+ '<img width="' + tt_aV[BALLOONEDGESIZE] + '" height="100%" src="' + aImg[8].src + sImgZ
				+ '</td>'
				// Content
				+ '<td style="position:relative;line-height:normal;'
				+ ';background-image:url(' + aImg[0].src + ')'
				+ ';color:' + tt_aV[FONTCOLOR]
				+ ';font-family:' + tt_aV[FONTFACE]
				+ ';font-size:' + tt_aV[FONTSIZE]
				+ ';font-weight:' + tt_aV[FONTWEIGHT]
				+ ';text-align:' + tt_aV[TEXTALIGN]
				+ ';padding:' + balloon.padding
				+ ';width:' + (balloon.width ? (balloon.width + 'px') : 'auto')
				+ ';">' + tt_sContent + '</td>'
				// Right border
				+ '<td style="position:relative;padding:0px;margin:0px;width:' + tt_aV[BALLOONEDGESIZE] + 'px;overflow:hidden;background-image:url(' + aImg[4].src + ');">'
				// Image redundancy for bugous old Geckos that won't auto-expand TD height to 100%
				+ '<img width="' + tt_aV[BALLOONEDGESIZE] + '" height="100%" src="' + aImg[4].src + sImgZ
				+ '</td>'
				+ '</tr><tr>'
				// Left-bottom corner
				+ '<td valign="top"' + sCssCrn + '>'
				+ '<img src="' + aImg[7].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
				+ '</td>'
				// Bottom border
				+ '<td valign="top" style="position:relative;padding:0px;margin:0px;overflow:hidden;">'
				+ '<div style="position:relative;left:0px;top:0px;padding:0px;margin:0px;overflow:hidden;width:auto;height:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[6].src + ');"></div>'
				+ '<img id="bALlOOnB" style="position:relative;top:-1px;left:2px;z-index:1;display:none;' + sCssImg + '" src="' + aImg[10].src + '" width="' + tt_aV[BALLOONSTEMWIDTH] + '" height="' + tt_aV[BALLOONSTEMHEIGHT] + '" />'
				+ '</td>'
				// Right-bottom corner
				+ '<td valign="top"' + sCssCrn + '>'
				+ '<img src="' + aImg[5].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
				+ '</td>'
				+ '</tr></table>';
	return true;
};
balloon.OnSubDivsCreated = function()
{
	if(tt_aV[BALLOON])
	{
		balloon.iStem = tt_aV[ABOVE] * 1;
		balloon.stem = [tt_GetElt("bALlOOnT"), tt_GetElt("bALlOOnB")];
		balloon.stem[balloon.iStem].style.display = "inline";
		return true;
	}
	return false;
};
// Display the stem appropriately
balloon.OnMoveAfter = function()
{
	if(tt_aV[BALLOON])
	{
		var iStem = (tt_aV[ABOVE] != tt_bJmpVert) * 1;

		// Tooltip position vertically flipped?
		if(iStem != balloon.iStem)
		{
			// Display opposite stem
			balloon.stem[balloon.iStem].style.display = "none";
			balloon.stem[iStem].style.display = "inline";
			balloon.iStem = iStem;
		}
		
		balloon.stem[iStem].style.left = Balloon_CalcStemX() + "px";
		return true;
	}
	return false;
};
function Balloon_CalcStemX()
{
	var x = tt_musX - tt_x;
	return Math.max(Math.min(x, tt_w - tt_aV[BALLOONSTEMWIDTH] - (tt_aV[BALLOONEDGESIZE] << 1) - 2), 2);
}
function Balloon_CacheImgs(sPath)
{
	var asImg = ["background", "lt", "t", "rt", "r", "rb", "b", "lb", "l", "stemt", "stemb"],
	n = asImg.length,
	aImg = new Array(n),
	img;

	while(n)
	{--n;
		img = aImg[n] = new Image();
		img.src = sPath + asImg[n] + ".gif";
	}
	return aImg;
}
// This mechanism pre-caches the default images specified by
// congif.BalloonImgPath, so, whenever a balloon tip using these default images
// is created, no further server connection is necessary.
function Balloon_PreCacheDefImgs()
{
	// Append slash to img path if missing
	if(config.BalloonImgPath.charAt(config.BalloonImgPath.length - 1) != '/')
		config.BalloonImgPath += "/";
	// Preload default images into array
	balloon.aDefImg = Balloon_CacheImgs(config.BalloonImgPath);
}
Balloon_PreCacheDefImgs();
