/*  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 = ''; 
     } 
   }, 
  
   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'; 
   } 
 } 
