/*  Prototype JavaScript framework, version 1.3.0 
 *  (c) 2005 Sam Stephenson <sam@conio.net> 
 * 
 *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff 
 *  against the source tree, available from the Prototype darcs repository.  
 * 
 *  Prototype is freely distributable under the terms of an MIT-style license. 
 * 
 *  For details, see the Prototype web site: http://prototype.conio.net/ 
 * 
/*--------------------------------------------------------------------------*/ 
 
var Prototype = { 
  Version: '1.3.0', 
  emptyFunction: function() {} 
} 
 
var Class = { 
  create: function() { 
    return function() {  
      this.initialize.apply(this, arguments); 
    } 
  } 
} 
 
var Abstract = new Object(); 
 
Object.extend = function(destination, source) { 
  for (property in source) { 
    destination[property] = source[property]; 
  } 
  return destination; 
} 
/* 
Object.prototype.extend = function(object) { 
  return Object.extend.apply(this, [this, object]); 
} 
*/ 
Function.prototype.bind = function(object) { 
  var __method = this; 
  return function() { 
    __method.apply(object, arguments); 
  } 
} 
 
Function.prototype.bindAsEventListener = function(object) { 
  var __method = this; 
  return function(event) { 
    __method.call(object, event || window.event); 
  } 
} 
 
Number.prototype.toColorPart = function() { 
  var digits = this.toString(16); 
  if (this < 16) return '0' + digits; 
  return digits; 
} 
 
var Try = { 
  these: function() { 
    var returnValue; 
 
    for (var i = 0; i < arguments.length; i++) { 
      var lambda = arguments[i]; 
      try { 
        returnValue = lambda(); 
        break; 
      } catch (e) {} 
    } 
 
    return returnValue; 
  } 
} 
 
/*--------------------------------------------------------------------------*/ 
 
var PeriodicalExecuter = Class.create(); 
PeriodicalExecuter.prototype = { 
  initialize: function(callback, frequency) { 
    this.callback = callback; 
    this.frequency = frequency; 
    this.currentlyExecuting = false; 
 
    this.registerCallback(); 
  }, 
 
  registerCallback: function() { 
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 
  }, 
 
  onTimerEvent: function() { 
    if (!this.currentlyExecuting) { 
      try {  
        this.currentlyExecuting = true; 
        this.callback();  
      } finally {  
        this.currentlyExecuting = false; 
      } 
    } 
  } 
} 
 
/*--------------------------------------------------------------------------*/ 
 
function $() { 
  var elements = new Array(); 
 
  for (var i = 0; i < arguments.length; i++) { 
    var element = arguments[i]; 
    if (typeof element == 'string') 
      element = document.getElementById(element); 
 
    if (arguments.length == 1)  
      return element; 
 
    elements.push(element); 
  } 
 
  return elements; 
} 
 
if (!Array.prototype.push) { 
  Array.prototype.push = function() { 
                var startLength = this.length; 
                for (var i = 0; i < arguments.length; i++) 
      this[startLength + i] = arguments[i]; 
          return this.length; 
  } 
} 
 
if (!Function.prototype.apply) { 
  // Based on code from http://www.youngpup.net/ 
  Function.prototype.apply = function(object, parameters) { 
    var parameterStrings = new Array(); 
    if (!object)     object = window; 
    if (!parameters) parameters = new Array(); 
     
    for (var i = 0; i < parameters.length; i++) 
      parameterStrings[i] = 'parameters[' + i + ']'; 
     
    object.__apply__ = this; 
    var result = eval('object.__apply__(' +  
      parameterStrings[i].join(', ') + ')'); 
    object.__apply__ = null; 
     
    return result; 
  } 
} 
 
Object.extend(String.prototype, { 
  stripTags: function() { 
    return this.replace(/<\/?[^>]+>/gi, ''); 
  }, 
 
  escapeHTML: function() { 
    var div = document.createElement('div'); 
    var text = document.createTextNode(this); 
    div.appendChild(text); 
    return div.innerHTML; 
  }, 
 
  unescapeHTML: function() { 
    var div = document.createElement('div'); 
    div.innerHTML = this.stripTags(); 
    return div.childNodes[0].nodeValue; 
  } 
}); 
 
var Ajax = { 
  getTransport: function() { 
    return Try.these( 
      function() {return new ActiveXObject('Msxml2.XMLHTTP')}, 
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}, 
      function() {return new XMLHttpRequest()} 
    ) || false; 
  } 
} 
 
Ajax.Base = function() {}; 
Ajax.Base.prototype = { 
  setOptions: function(options) { 
    this.options = Object.extend({ 
      method:       'post', 
      asynchronous: true, 
      parameters:   '' 
    }, options || {}); 
  }, 
 
  responseIsSuccess: function() { 
    return this.transport.status == undefined 
        || this.transport.status == 0  
        || (this.transport.status >= 200 && this.transport.status < 300); 
  }, 
 
  responseIsFailure: function() { 
    return !this.responseIsSuccess(); 
  } 
} 
 
Ajax.Request = Class.create(); 
Ajax.Request.Events =  
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
 
Ajax.Request.prototype = Object.extend(new Ajax.Base(), { 
  initialize: function(url, options) { 
    this.transport = Ajax.getTransport(); 
    this.setOptions(options); 
    this.request(url); 
  }, 
 
  request: function(url) { 
    var parameters = this.options.parameters || ''; 
    if (parameters.length > 0) parameters += '&_='; 
 
    try { 
      if (this.options.method == 'get') 
        url += '?' + parameters; 
 
      this.transport.open(this.options.method, url, 
        this.options.asynchronous); 
 
      if (this.options.asynchronous) { 
        this.transport.onreadystatechange = this.onStateChange.bind(this); 
        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); 
      } 
 
      this.setRequestHeaders(); 
 
      var body = this.options.postBody ? this.options.postBody : parameters; 
      this.transport.send(this.options.method == 'post' ? body : null); 
 
    } catch (e) { 
    } 
  }, 
 
  setRequestHeaders: function() { 
    var requestHeaders =  
      ['X-Requested-With', 'XMLHttpRequest', 
       'X-Prototype-Version', Prototype.Version]; 
 
    if (this.options.method == 'post') { 
      requestHeaders.push('Content-type',  
        'application/x-www-form-urlencoded'); 
 
      /* Force "Connection: close" for Mozilla browsers to work around 
       * a bug where XMLHttpReqeuest sends an incorrect Content-length 
       * header. See Mozilla Bugzilla #246651.  
       */ 
      if (this.transport.overrideMimeType) 
        requestHeaders.push('Connection', 'close'); 
    } 
 
    if (this.options.requestHeaders) 
      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); 
 
    for (var i = 0; i < requestHeaders.length; i += 2) 
      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); 
  }, 
 
  onStateChange: function() { 
    var readyState = this.transport.readyState; 
    if (readyState != 1) 
      this.respondToReadyState(this.transport.readyState); 
  }, 
 
  respondToReadyState: function(readyState) { 
    var event = Ajax.Request.Events[readyState]; 
 
    if (event == 'Complete') 
      (this.options['on' + this.transport.status] 
       || this.options['on' + this.responseIsSuccess ? 'Success' : 'Failure'] 
       || Prototype.emptyFunction)(this.transport);        
 
    (this.options['on' + event] || Prototype.emptyFunction)(this.transport); 
 
    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ 
    if (event == 'Complete') 
      this.transport.onreadystatechange = Prototype.emptyFunction; 
  } 
}); 
 
Ajax.Updater = Class.create(); 
Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)'; 
 
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { 
  initialize: function(container, url, options) { 
    this.containers = { 
      success: container.success ? $(container.success) : $(container), 
      failure: container.failure ? $(container.failure) : 
        (container.success ? null : $(container)) 
    } 
 
    this.transport = Ajax.getTransport(); 
    this.setOptions(options); 
 
    var onComplete = this.options.onComplete || Prototype.emptyFunction; 
    this.options.onComplete = (function() { 
      this.updateContent(); 
      onComplete(this.transport);       
    }).bind(this); 
 
    this.request(url); 
  }, 
 
  updateContent: function() { 
    var receiver = this.responseIsSuccess() ? 
      this.containers.success : this.containers.failure; 
 
    var match    = new RegExp(Ajax.Updater.ScriptFragment, 'img'); 
    var response = this.transport.responseText.replace(match, ''); 
    var scripts  = this.transport.responseText.match(match); 
 
    if (receiver) { 
      if (this.options.insertion) { 
        new this.options.insertion(receiver, response); 
      } else { 
        receiver.innerHTML = response; 
      } 
    } 
 
    if (this.responseIsSuccess()) { 
      if (this.onComplete) 
        setTimeout((function() {this.onComplete( 
          this.transport)}).bind(this), 10); 
    } 
 
    if (this.options.evalScripts && scripts) { 
      match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); 
      setTimeout((function() { 
        for (var i = 0; i < scripts.length; i++) 
          eval(scripts[i].match(match)[1]); 
      }).bind(this), 10); 
    } 
  } 
}); 
 
Ajax.PeriodicalUpdater = Class.create(); 
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { 
  initialize: function(container, url, options) { 
    this.setOptions(options); 
    this.onComplete = this.options.onComplete; 
 
    this.frequency = (this.options.frequency || 2); 
    this.decay = 1; 
 
    this.updater = {}; 
    this.container = container; 
    this.url = url; 
 
    this.start(); 
  }, 
 
  start: function() { 
    this.options.onComplete = this.updateComplete.bind(this); 
    this.onTimerEvent(); 
  }, 
 
  stop: function() { 
    this.updater.onComplete = undefined; 
    clearTimeout(this.timer); 
    (this.onComplete || Ajax.emptyFunction).apply(this, arguments); 
  }, 
 
  updateComplete: function(request) { 
    if (this.options.decay) { 
      this.decay = (request.responseText == this.lastText ?  
        this.decay * this.options.decay : 1); 
 
      this.lastText = request.responseText; 
    } 
    this.timer = setTimeout(this.onTimerEvent.bind(this),  
      this.decay * this.frequency * 1000); 
  }, 
 
  onTimerEvent: function() { 
    this.updater = new Ajax.Updater(this.container, this.url, this.options); 
  } 
}); 
 
document.getElementsByClassName = function(className) { 
  var children = document.getElementsByTagName('*') || document.all; 
  var elements = new Array(); 
   
  for (var i = 0; i < children.length; i++) { 
    var child = children[i]; 
    var classNames = child.className.split(' '); 
    for (var j = 0; j < classNames.length; j++) { 
      if (classNames[j] == className) { 
        elements.push(child); 
        break; 
      } 
    } 
  } 
   
  return elements; 
} 
 
/*--------------------------------------------------------------------------*/ 
 
if (!window.Element) { 
  var Element = new Object(); 
} 
 
Object.extend(Element, { 
  toggle: function() { 
    for (var i = 0; i < arguments.length; i++) { 
      var element = $(arguments[i]); 
      element.style.display =  
        (element.style.display == 'none' ? '' : 'none'); 
    } 
  }, 
 
  hide: function() { 
    for (var i = 0; i < arguments.length; i++) { 
      var element = $(arguments[i]); 
      element.style.display = 'none'; 
    } 
  }, 
 
  show: function() { 
    for (var i = 0; i < arguments.length; i++) { 
      var element = $(arguments[i]); 
      element.style.display = 'block'; 
    } 
  }, 
 
  remove: function(element) { 
    element = $(element); 
    element.parentNode.removeChild(element); 
  }, 
    
  getHeight: function(element) { 
    element = $(element); 
    return element.offsetHeight;  
  }, 
 
  hasClassName: function(element, className) { 
    element = $(element); 
    if (!element) 
      return; 
    var a = element.className.split(' '); 
    for (var i = 0; i < a.length; i++) { 
      if (a[i] == className) 
        return true; 
    } 
    return false; 
  }, 
 
  addClassName: function(element, className) { 
    element = $(element); 
    Element.removeClassName(element, className); 
    element.className += ' ' + className; 
  }, 
 
  removeClassName: function(element, className) { 
    element = $(element); 
    if (!element) 
      return; 
    var newClassName = ''; 
    var a = element.className.split(' '); 
    for (var i = 0; i < a.length; i++) { 
      if (a[i] != className) { 
        if (i > 0) 
          newClassName += ' '; 
        newClassName += a[i]; 
      } 
    } 
    element.className = newClassName; 
  }, 
   
  // removes whitespace-only text node children 
  cleanWhitespace: function(element) { 
    var element = $(element); 
    for (var i = 0; i < element.childNodes.length; i++) { 
      var node = element.childNodes[i]; 
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))  
        Element.remove(node); 
    } 
  } 
}); 
 
var Toggle = new Object(); 
Toggle.display = Element.toggle; 
 
/*--------------------------------------------------------------------------*/ 
 
Abstract.Insertion = function(adjacency) { 
  this.adjacency = adjacency; 
} 
 
Abstract.Insertion.prototype = { 
  initialize: function(element, content) { 
    this.element = $(element); 
    this.content = content; 
     
    if (this.adjacency && this.element.insertAdjacentHTML) { 
      this.element.insertAdjacentHTML(this.adjacency, this.content); 
    } else { 
      this.range = this.element.ownerDocument.createRange(); 
      if (this.initializeRange) this.initializeRange(); 
      this.fragment = this.range.createContextualFragment(this.content); 
      this.insertContent(); 
    } 
  } 
} 
 
var Insertion = new Object(); 
 
Insertion.Before = Class.create(); 
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { 
  initializeRange: function() { 
    this.range.setStartBefore(this.element); 
  }, 
   
  insertContent: function() { 
    this.element.parentNode.insertBefore(this.fragment, this.element); 
  } 
}); 
 
Insertion.Top = Class.create(); 
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { 
  initializeRange: function() { 
    this.range.selectNodeContents(this.element); 
    this.range.collapse(true); 
  }, 
   
  insertContent: function() {   
    this.element.insertBefore(this.fragment, this.element.firstChild); 
  } 
}); 
 
Insertion.Bottom = Class.create(); 
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { 
  initializeRange: function() { 
    this.range.selectNodeContents(this.element); 
    this.range.collapse(this.element); 
  }, 
   
  insertContent: function() { 
    this.element.appendChild(this.fragment); 
  } 
}); 
 
Insertion.After = Class.create(); 
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { 
  initializeRange: function() { 
    this.range.setStartAfter(this.element); 
  }, 
   
  insertContent: function() { 
    this.element.parentNode.insertBefore(this.fragment,  
      this.element.nextSibling); 
  } 
}); 
 
var Field = { 
  clear: function() { 
    for (var i = 0; i < arguments.length; i++) 
      $(arguments[i]).value = ''; 
  }, 
 
  focus: function(element) { 
    $(element).focus(); 
  }, 
   
  present: function() { 
    for (var i = 0; i < arguments.length; i++) 
      if ($(arguments[i]).value == '') return false; 
    return true; 
  }, 
   
  select: function(element) { 
    $(element).select(); 
  }, 
    
  activate: function(element) { 
    $(element).focus(); 
    $(element).select(); 
  } 
} 
 
/*--------------------------------------------------------------------------*/ 
 
var Form = { 
  serialize: function(form) { 
    var elements = Form.getElements($(form)); 
    var queryComponents = new Array(); 
     
    for (var i = 0; i < elements.length; i++) { 
      var queryComponent = Form.Element.serialize(elements[i]); 
      if (queryComponent) 
        queryComponents.push(queryComponent); 
    } 
     
    return queryComponents.join('&'); 
  }, 
   
  getElements: function(form) { 
    var form = $(form); 
    var elements = new Array(); 
 
    for (tagName in Form.Element.Serializers) { 
      var tagElements = form.getElementsByTagName(tagName); 
      for (var j = 0; j < tagElements.length; j++) 
        elements.push(tagElements[j]); 
    } 
    return elements; 
  }, 
   
  getInputs: function(form, typeName, name) { 
    var form = $(form); 
    var inputs = form.getElementsByTagName('input'); 
     
    if (!typeName && !name) 
      return inputs; 
       
    var matchingInputs = new Array(); 
    for (var i = 0; i < inputs.length; i++) { 
      var input = inputs[i]; 
      if ((typeName && input.type != typeName) || 
          (name && input.name != name))  
        continue; 
      matchingInputs.push(input); 
    } 
 
    return matchingInputs; 
  }, 
 
  disable: function(form) { 
    var elements = Form.getElements(form); 
    for (var i = 0; i < elements.length; i++) { 
      var element = elements[i]; 
      element.blur(); 
      element.disabled = 'true'; 
    } 
  }, 
 
  enable: function(form) { 
    var elements = Form.getElements(form); 
    for (var i = 0; i < elements.length; i++) { 
      var element = elements[i]; 
      element.disabled = ''; 
    } 
  }, 
 
  focusFirstElement: function(form) { 
    var form = $(form); 
    var elements = Form.getElements(form); 
    for (var i = 0; i < elements.length; i++) { 
      var element = elements[i]; 
      if (element.type != 'hidden' && !element.disabled) { 
        Field.activate(element); 
        break; 
      } 
    } 
  }, 
 
  reset: function(form) { 
    $(form).reset(); 
  } 
} 
 
Form.Element = { 
  serialize: function(element) { 
    var element = $(element); 
    var method = element.tagName.toLowerCase(); 
    var parameter = Form.Element.Serializers[method](element); 
     
    if (parameter) 
      return encodeURIComponent(parameter[0]) + '=' +  
        encodeURIComponent(parameter[1]);                    
  }, 
   
  getValue: function(element) { 
    var element = $(element); 
    var method = element.tagName.toLowerCase(); 
    var parameter = Form.Element.Serializers[method](element); 
     
    if (parameter)  
      return parameter[1]; 
  } 
} 
 
Form.Element.Serializers = { 
  input: function(element) { 
    switch (element.type.toLowerCase()) { 
      case 'submit': 
      case 'hidden': 
      case 'password': 
      case 'text': 
        return Form.Element.Serializers.textarea(element); 
      case 'checkbox':   
      case 'radio': 
        return Form.Element.Serializers.inputSelector(element); 
    } 
    return false; 
  }, 
 
  inputSelector: function(element) { 
    if (element.checked) 
      return [element.name, element.value]; 
  }, 
 
  textarea: function(element) { 
    return [element.name, element.value]; 
  }, 
 
  select: function(element) { 
    var value = ''; 
    if (element.type == 'select-one') { 
      var index = element.selectedIndex; 
      if (index >= 0) 
        value = element.options[index].value || element.options[index].text; 
    } else { 
      value = new Array(); 
      for (var i = 0; i < element.length; i++) { 
        var opt = element.options[i]; 
        if (opt.selected) 
          value.push(opt.value || opt.text); 
      } 
    } 
    return [element.name, value]; 
  } 
} 
 
/*--------------------------------------------------------------------------*/ 
 
var $F = Form.Element.getValue; 
 
/*--------------------------------------------------------------------------*/ 
 
Abstract.TimedObserver = function() {} 
Abstract.TimedObserver.prototype = { 
  initialize: function(element, frequency, callback) { 
    this.frequency = frequency; 
    this.element   = $(element); 
    this.callback  = callback; 
     
    this.lastValue = this.getValue(); 
    this.registerCallback(); 
  }, 
   
  registerCallback: function() { 
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 
  }, 
   
  onTimerEvent: function() { 
    var value = this.getValue(); 
    if (this.lastValue != value) { 
      this.callback(this.element, value); 
      this.lastValue = value; 
    } 
  } 
} 
 
Form.Element.Observer = Class.create(); 
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
  getValue: function() { 
    return Form.Element.getValue(this.element); 
  } 
}); 
 
Form.Observer = Class.create(); 
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
  getValue: function() { 
    return Form.serialize(this.element); 
  } 
}); 
 
/*--------------------------------------------------------------------------*/ 
 
Abstract.EventObserver = function() {} 
Abstract.EventObserver.prototype = { 
  initialize: function(element, callback) { 
    this.element  = $(element); 
    this.callback = callback; 
     
    this.lastValue = this.getValue(); 
    if (this.element.tagName.toLowerCase() == 'form') 
      this.registerFormCallbacks(); 
    else 
      this.registerCallback(this.element); 
  }, 
   
  onElementEvent: function() { 
    var value = this.getValue(); 
    if (this.lastValue != value) { 
      this.callback(this.element, value); 
      this.lastValue = value; 
    } 
  }, 
   
  registerFormCallbacks: function() { 
    var elements = Form.getElements(this.element); 
    for (var i = 0; i < elements.length; i++) 
      this.registerCallback(elements[i]); 
  }, 
   
  registerCallback: function(element) { 
    if (element.type) { 
      switch (element.type.toLowerCase()) { 
        case 'checkbox':   
        case 'radio': 
          element.target = this; 
          element.prev_onclick = element.onclick || Prototype.emptyFunction; 
          element.onclick = function() { 
            this.prev_onclick();  
            this.target.onElementEvent(); 
          } 
          break; 
        case 'password': 
        case 'text': 
        case 'textarea': 
        case 'select-one': 
        case 'select-multiple': 
          element.target = this; 
          element.prev_onchange = element.onchange || Prototype.emptyFunction; 
          element.onchange = function() { 
            this.prev_onchange();  
            this.target.onElementEvent(); 
          } 
          break; 
      } 
    }     
  } 
} 
 
Form.Element.EventObserver = Class.create(); 
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
  getValue: function() { 
    return Form.Element.getValue(this.element); 
  } 
}); 
 
Form.EventObserver = Class.create(); 
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
  getValue: function() { 
    return Form.serialize(this.element); 
  } 
}); 
 
 
if (!window.Event) { 
  var Event = new Object(); 
} 
 
Object.extend(Event, { 
  KEY_BACKSPACE: 8, 
  KEY_TAB:       9, 
  KEY_RETURN:   13, 
  KEY_ESC:      27, 
  KEY_LEFT:     37, 
  KEY_UP:       38, 
  KEY_RIGHT:    39, 
  KEY_DOWN:     40, 
  KEY_DELETE:   46, 
 
  element: function(event) { 
    return event.target || event.srcElement; 
  }, 
 
  isLeftClick: function(event) { 
    return (((event.which) && (event.which == 1)) || 
            ((event.button) && (event.button == 1))); 
  }, 
 
  pointerX: function(event) { 
    return event.pageX || (event.clientX +  
      (document.documentElement.scrollLeft || document.body.scrollLeft)); 
  }, 
 
  pointerY: function(event) { 
    return event.pageY || (event.clientY +  
      (document.documentElement.scrollTop || document.body.scrollTop)); 
  }, 
 
  stop: function(event) { 
    if (event.preventDefault) {  
      event.preventDefault();  
      event.stopPropagation();  
    } else { 
      event.returnValue = false; 
    } 
  }, 
 
  // find the first node with the given tagName, starting from the 
  // node the event was triggered on; traverses the DOM upwards 
  findElement: function(event, tagName) { 
    var element = Event.element(event); 
    while (element.parentNode && (!element.tagName || 
        (element.tagName.toUpperCase() != tagName.toUpperCase()))) 
      element = element.parentNode; 
    return element; 
  }, 
   
  observers: false, 
   
  _observeAndCache: function(element, name, observer, useCapture) { 
    if(!this.observers) this.observers = []; 
    if(element.addEventListener) { 
      this.observers.push([element,name,observer,useCapture]); 
      element.addEventListener(name, observer, useCapture); 
    } else if (element.attachEvent) { 
      this.observers.push([element,name,observer,useCapture]); 
      element.attachEvent('on'+name, observer); 
    } 
  }, 
   
  unloadCache: function() { 
    if(!Event.observers) return; 
    for(var i=0; i<Event.observers.length; i++) { 
      Event.stopObserving(Event.observers[i][0],Event.observers[i][1],Event.observers[i][2],Event.observers[i][3]); 
      Event.observers[i][0] = null; 
    } 
    Event.observers = false; 
  }, 
 
  observe: function(element, name, observer, useCapture) { 
    var element = $(element); 
    useCapture = useCapture || false; 
     
    if(name == 'keypress' && 
      ((navigator.appVersion.indexOf('AppleWebKit') > 0) || element.attachEvent)) 
        name = 'keydown'; 
     
    this._observeAndCache(element, name, observer, useCapture); 
  }, 
 
  stopObserving: function(element, name, observer, useCapture) { 
    var element = $(element); 
    useCapture = useCapture || false; 
     
    if(name == 'keypress' && 
      ((navigator.appVersion.indexOf('AppleWebKit') > 0) || element.detachEvent)) 
        name = 'keydown'; 
     
    if (element.removeEventListener) { 
      element.removeEventListener(name, observer, useCapture); 
    } else if (element.detachEvent) { 
      element.detachEvent('on' + name, observer); 
    } 
  } 
}); 
 
// prevent memory leaks 
Event.observe(window,'unload', Event.unloadCache, false); 
 
var Position = { 
 
  // set to true if needed, warning: firefox performance problems 
  // NOT neeeded for page scrolling, only if draggable contained in 
  // scrollable elements 
  includeScrollOffsets: false,  
 
  // must be called before calling withinIncludingScrolloffset, every time the 
  // page is scrolled 
  prepare: function() { 
    this.deltaX =  window.pageXOffset  
                || document.documentElement.scrollLeft  
                || document.body.scrollLeft  
                || 0; 
    this.deltaY =  window.pageYOffset  
                || document.documentElement.scrollTop  
                || document.body.scrollTop  
                || 0; 
  }, 
 
  realOffset: function(element) { 
    var valueT = 0, valueL = 0; 
    do { 
      valueT += element.scrollTop  || 0; 
      valueL += element.scrollLeft || 0;  
      element = element.parentNode; 
    } while (element); 
    return [valueL, valueT]; 
  }, 
 
  cumulativeOffset: function(element) { 
    var valueT = 0, valueL = 0; 
    do { 
      valueT += element.offsetTop  || 0; 
      valueL += element.offsetLeft || 0; 
      element = element.offsetParent; 
    } while (element); 
    return [valueL, valueT]; 
  }, 
 
  // caches x/y coordinate pair to use with overlap 
  within: function(element, x, y) { 
    if (this.includeScrollOffsets) 
      return this.withinIncludingScrolloffsets(element, x, y); 
    this.xcomp = x; 
    this.ycomp = y; 
    this.offset = this.cumulativeOffset(element); 
 
    return (y >= this.offset[1] && 
            y <  this.offset[1] + element.offsetHeight && 
            x >= this.offset[0] &&  
            x <  this.offset[0] + element.offsetWidth); 
  }, 
 
  withinIncludingScrolloffsets: function(element, x, y) { 
    var offsetcache = this.realOffset(element); 
 
    this.xcomp = x + offsetcache[0] - this.deltaX; 
    this.ycomp = y + offsetcache[1] - this.deltaY; 
    this.offset = this.cumulativeOffset(element); 
 
    return (this.ycomp >= this.offset[1] && 
            this.ycomp <  this.offset[1] + element.offsetHeight && 
            this.xcomp >= this.offset[0] &&  
            this.xcomp <  this.offset[0] + element.offsetWidth); 
  }, 
 
  // within must be called directly before 
  overlap: function(mode, element) {   
    if (!mode) return 0;   
    if (mode == 'vertical')  
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /  
        element.offsetHeight; 
    if (mode == 'horizontal') 
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /  
        element.offsetWidth; 
  }, 
 
  clone: function(source, target) { 
    source = $(source); 
    target = $(target); 
    target.style.position = 'absolute'; 
    var offsets = this.cumulativeOffset(source); 
    target.style.top    = offsets[1] + 'px'; 
    target.style.left   = offsets[0] + 'px'; 
    target.style.width  = source.offsetWidth + 'px'; 
    target.style.height = source.offsetHeight + 'px'; 
  } 
} 
