/* Prototype JavaScript framework,version 1.4.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.4.0',
 ScriptFragment:'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

 emptyFunction:function(){},
 K:function(x){return x}
}

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.inspect = function(object){
 try{
 if (object == undefined) return 'undefined';
 if (object == null) return 'null';
 return object.inspect ? object.inspect() :object.toString();
}catch (e){
 if (e instanceof RangeError) return '...';
 throw e;
}
}

Function.prototype.bind = function(){
 var __method = this,args = $A(arguments),object = args.shift();
 return function(){
 return __method.apply(object,args.concat($A(arguments)));
}
}

Function.prototype.bindAsEventListener = function(object){
 var __method = this;
 return function(event){
 return __method.call(object,event || window.event);
}
}

Object.extend(Number.prototype,{
 toColorPart:function(){
 var digits = this.toString(16);
 if (this < 16) return '0' + digits;
 return digits;
},

 succ:function(){
 return this + 1;
},

 times:function(iterator){
 $R(0,this,true).each(iterator);
 return this;
}
});

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;
}
Object.extend(String.prototype,{
 stripTags:function(){
 return this.replace(/<\/?[^>]+>/gi,'');
},

 stripScripts:function(){
 return this.replace(new RegExp(Prototype.ScriptFragment,'img'),'');
},

 extractScripts:function(){
 var matchAll = new RegExp(Prototype.ScriptFragment,'img');
 var matchOne = new RegExp(Prototype.ScriptFragment,'im');
 return (this.match(matchAll) || []).map(function(scriptTag){
  return (scriptTag.match(matchOne) || ['',''])[1];
});
},

 evalScripts:function(){
 return this.extractScripts().map(eval);
},

 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] ? div.childNodes[0].nodeValue :'';
},

 toQueryParams:function(){
 var pairs = this.match(/^\??(.*)$/)[1].split('&');
 return pairs.inject({},function(params,pairString){
  var pair = pairString.split('=');
  params[pair[0]] = pair[1];
  return params;
});
},

 toArray:function(){
 return this.split('');
},

 camelize:function(){
 var oStringList = this.split('-');
 if (oStringList.length == 1) return oStringList[0];

 var camelizedString = this.indexOf('-') == 0
  ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
  :oStringList[0];

 for (var i = 1,len = oStringList.length;i < len;i++){
  var s = oStringList[i];
  camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}

 return camelizedString;
},

 inspect:function(){
 return "'" + this.replace('\\','\\\\').replace("'",'\\\'') + "'";
}
});

String.prototype.parseQuery = String.prototype.toQueryParams;

var $break = new Object();
var $continue = new Object();

var Enumerable ={
 each:function(iterator){
 var index = 0;
 try{
  this._each(function(value){
  try{
   iterator(value,index++);
 }catch (e){
   if (e != $continue) throw e;
 }
 });
}catch (e){
  if (e != $break) throw e;
}
},

 all:function(iterator){
 var result = true;
 this.each(function(value,index){
  result = result && !!(iterator || Prototype.K)(value,index);
  if (!result) throw $break;
});
 return result;
},

 any:function(iterator){
 var result = true;
 this.each(function(value,index){
  if (result = !!(iterator || Prototype.K)(value,index))
  throw $break;
});
 return result;
},

 collect:function(iterator){
 var results = [];
 this.each(function(value,index){
  results.push(iterator(value,index));
});
 return results;
},

 detect:function (iterator){
 var result;
 this.each(function(value,index){
  if (iterator(value,index)){
  result = value;
  throw $break;
 }
});
 return result;
},

 findAll:function(iterator){
 var results = [];
 this.each(function(value,index){
  if (iterator(value,index))
  results.push(value);
});
 return results;
},

 grep:function(pattern,iterator){
 var results = [];
 this.each(function(value,index){
  var stringValue = value.toString();
  if (stringValue.match(pattern))
  results.push((iterator || Prototype.K)(value,index));
})
 return results;
},

 include:function(object){
 var found = false;
 this.each(function(value){
  if (value == object){
  found = true;
  throw $break;
 }
});
 return found;
},

 inject:function(memo,iterator){
 this.each(function(value,index){
  memo = iterator(memo,value,index);
});
 return memo;
},

 invoke:function(method){
 var args = $A(arguments).slice(1);
 return this.collect(function(value){
  return value[method].apply(value,args);
});
},

 max:function(iterator){
 var result;
 this.each(function(value,index){
  value = (iterator || Prototype.K)(value,index);
  if (value >= (result || value))
  result = value;
});
 return result;
},

 min:function(iterator){
 var result;
 this.each(function(value,index){
  value = (iterator || Prototype.K)(value,index);
  if (value <= (result || value))
  result = value;
});
 return result;
},

 partition:function(iterator){
 var trues = [],falses = [];
 this.each(function(value,index){
  ((iterator || Prototype.K)(value,index) ?
  trues :falses).push(value);
});
 return [trues,falses];
},

 pluck:function(property){
 var results = [];
 this.each(function(value,index){
  results.push(value[property]);
});
 return results;
},

 reject:function(iterator){
 var results = [];
 this.each(function(value,index){
  if (!iterator(value,index))
  results.push(value);
});
 return results;
},

 sortBy:function(iterator){
 return this.collect(function(value,index){
  return{value:value,criteria:iterator(value,index)};
}).sort(function(left,right){
  var a = left.criteria,b = right.criteria;
  return a < b ? -1 :a > b ? 1 :0;
}).pluck('value');
},

 toArray:function(){
 return this.collect(Prototype.K);
},

 zip:function(){
 var iterator = Prototype.K,args = $A(arguments);
 if (typeof args.last() == 'function')
  iterator = args.pop();

 var collections = [this].concat(args).map($A);
 return this.map(function(value,index){
  iterator(value = collections.pluck(index));
  return value;
});
},

 inspect:function(){
 return '#<Enumerable:' + this.toArray().inspect() + '>';
}
}

Object.extend(Enumerable,{
 map: Enumerable.collect,
 find:Enumerable.detect,
 select:Enumerable.findAll,
 member:Enumerable.include,
 entries:Enumerable.toArray
});
var $A = Array.from = function(iterable){
 if (!iterable) return [];
 if (iterable.toArray){
 return iterable.toArray();
}else{
 var results = [];
 for (var i = 0;i < iterable.length;i++)
  results.push(iterable[i]);
 return results;
}
}

Object.extend(Array.prototype,Enumerable);

Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype,{
 _each:function(iterator){
 for (var i = 0;i < this.length;i++)
  iterator(this[i]);
},

 clear:function(){
 this.length = 0;
 return this;
},

 first:function(){
 return this[0];
},

 last:function(){
 return this[this.length - 1];
},

 compact:function(){
 return this.select(function(value){
  return value != undefined || value != null;
});
},

 flatten:function(){
 return this.inject([],function(array,value){
  return array.concat(value.constructor == Array ?
  value.flatten() :[value]);
});
},

 without:function(){
 var values = $A(arguments);
 return this.select(function(value){
  return !values.include(value);
});
},

 indexOf:function(object){
 for (var i = 0;i < this.length;i++)
  if (this[i] == object) return i;
 return -1;
},

 reverse:function(inline){
 return (inline !== false ? this :this.toArray())._reverse();
},

 shift:function(){
 var result = this[0];
 for (var i = 0;i < this.length - 1;i++)
  this[i] = this[i + 1];
 this.length--;
 return result;
},

 inspect:function(){
 return '[' + this.map(Object.inspect).join(',') + ']';
}
});
var Hash ={
 _each:function(iterator){
 for (key in this){
  var value = this[key];
  if (typeof value == 'function') continue;

  var pair = [key,value];
  pair.key = key;
  pair.value = value;
  iterator(pair);
}
},

 keys:function(){
 return this.pluck('key');
},

 values:function(){
 return this.pluck('value');
},

 merge:function(hash){
 return $H(hash).inject($H(this),function(mergedHash,pair){
  mergedHash[pair.key] = pair.value;
  return mergedHash;
});
},

 toQueryString:function(){
 return this.map(function(pair){
  return pair.map(encodeURIComponent).join('=');
}).join('&');
},

 inspect:function(){
 return '#<Hash:{' + this.map(function(pair){
  return pair.map(Object.inspect).join(':');
}).join(',') + '}>';
}
}

function $H(object){
 var hash = Object.extend({},object ||{});
 Object.extend(hash,Enumerable);
 Object.extend(hash,Hash);
 return hash;
}
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype,Enumerable);
Object.extend(ObjectRange.prototype,{
 initialize:function(start,end,exclusive){
 this.start = start;
 this.end = end;
 this.exclusive = exclusive;
},

 _each:function(iterator){
 var value = this.start;
 do{
  iterator(value);
  value = value.succ();
}while (this.include(value));
},

 include:function(value){
 if (value < this.start)
  return false;
 if (this.exclusive)
  return value < this.end;
 return value <= this.end;
}
});

var $R = function(start,end,exclusive){
 return new ObjectRange(start,end,exclusive);
}

var Ajax ={
 getTransport:function(){
 return Try.these(
  function(){return new ActiveXObject('Msxml2.XMLHTTP')},
  function(){return new ActiveXObject('Microsoft.XMLHTTP')},
  function(){return new XMLHttpRequest()}
 ) || false;
},

 activeRequestCount:0
}

Ajax.Responders ={
 responders:[],

 _each:function(iterator){
 this.responders._each(iterator);
},

 register:function(responderToAdd){
 if (!this.include(responderToAdd))
  this.responders.push(responderToAdd);
},

 unregister:function(responderToRemove){
 this.responders = this.responders.without(responderToRemove);
},

 dispatch:function(callback,request,transport,json){
 this.each(function(responder){
  if (responder[callback] && typeof responder[callback] == 'function'){
  try{
   responder[callback].apply(responder,[request,transport,json]);
 }catch (e){}
 }
});
}
};

Object.extend(Ajax.Responders,Enumerable);

Ajax.Responders.register({
 onCreate:function(){
 Ajax.activeRequestCount++;
},

 onComplete:function(){
 Ajax.activeRequestCount--;
}
});

Ajax.Base = function(){};
Ajax.Base.prototype ={
 setOptions:function(options){
 this.options ={
  method: 'post',
  asynchronous:true,
  parameters:''
}
 Object.extend(this.options,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{
  this.url = url;
  if (this.options.method == 'get' && parameters.length > 0)
  this.url += (this.url.match(/\?/) ? '&' :'?') + parameters;

  Ajax.Responders.dispatch('onCreate',this,this.transport);

  this.transport.open(this.options.method,this.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){
  this.dispatchException(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);
},

 header:function(name){
 try{
  return this.transport.getResponseHeader(name);
}catch (e){}
},

 evalJSON:function(){
 try{
  return eval(this.header('X-JSON'));
}catch (e){}
},

 evalResponse:function(){
 try{
  return eval(this.transport.responseText);
}catch (e){
  this.dispatchException(e);
}
},

 respondToReadyState:function(readyState){
 var event = Ajax.Request.Events[readyState];
 var transport = this.transport,json = this.evalJSON();

 if (event == 'Complete'){
  try{
  (this.options['on' + this.transport.status]
   || this.options['on' + (this.responseIsSuccess() ? 'Success' :'Failure')]
   || Prototype.emptyFunction)(transport,json);
 }catch (e){
  this.dispatchException(e);
 }

  if ((this.header('Content-type') || '').match(/^text\/javascript/i))
  this.evalResponse();
}

 try{
  (this.options['on' + event] || Prototype.emptyFunction)(transport,json);
  Ajax.Responders.dispatch('on' + event,this,transport,json);
}catch (e){
  this.dispatchException(e);
}

 /* Avoid memory leak in MSIE:clean up the oncomplete event handler */
 if (event == 'Complete')
  this.transport.onreadystatechange = Prototype.emptyFunction;
},

 dispatchException:function(exception){
 (this.options.onException || Prototype.emptyFunction)(this,exception);
 Ajax.Responders.dispatch('onException',this,exception);
}
});

Ajax.Updater = Class.create();

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(transport,object){
  this.updateContent();
  onComplete(transport,object);
}).bind(this);

 this.request(url);
},

 updateContent:function(){
 var receiver = this.responseIsSuccess() ?
  this.containers.success :this.containers.failure;
 var response = this.transport.responseText;

 if (!this.options.evalScripts)
  response = response.stripScripts();

 if (receiver){
  if (this.options.insertion){
  new this.options.insertion(receiver,response);
 }else{
  Element.update(receiver,response);
 }
}

 if (this.responseIsSuccess()){
  if (this.onComplete)
  setTimeout(this.onComplete.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 = (this.options.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 || Prototype.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,parentElement){
 var children = ($(parentElement) || document.body).getElementsByTagName('*');
 return $A(children).inject([],function(elements,child){
 if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
  elements.push(child);
 return elements;
});
}

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

if (!window.Element){
 var Element = new Object();
}

Object.extend(Element,{
 visible:function(element){
 return $(element).style.display != 'none';
},

 toggle:function(){
 for (var i = 0;i < arguments.length;i++){
  var element = $(arguments[i]);
  Element[Element.visible(element) ? 'hide' :'show'](element);
}
},

 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);
},

 update:function(element,html){
 $(element).innerHTML = html.stripScripts();
 setTimeout(function(){html.evalScripts()},10);
},

 getHeight:function(element){
 element = $(element);
 return element.offsetHeight;
},

 classNames:function(element){
 return new Element.ClassNames(element);
},

 hasClassName:function(element,className){
 if (!(element = $(element))) return;
 return Element.classNames(element).include(className);
},

 addClassName:function(element,className){
 if (!(element = $(element))) return;
 return Element.classNames(element).add(className);
},

 removeClassName:function(element,className){
 if (!(element = $(element))) return;
 return Element.classNames(element).remove(className);
},

 // removes whitespace-only text node children
 cleanWhitespace:function(element){
 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);
}
},

 empty:function(element){
 return $(element).innerHTML.match(/^\s*$/);
},

 scrollTo:function(element){
 element = $(element);
 var x = element.x ? element.x :element.offsetLeft,
  y = element.y ? element.y :element.offsetTop;
 window.scrollTo(x,y);
},

 getStyle:function(element,style){
 element = $(element);
 var value = element.style[style.camelize()];
 if (!value){
  if (document.defaultView && document.defaultView.getComputedStyle){
  var css = document.defaultView.getComputedStyle(element,null);
  value = css ? css.getPropertyValue(style) :null;
 }else if (element.currentStyle){
  value = element.currentStyle[style.camelize()];
 }
}

 if (window.opera && ['left','top','right','bottom'].include(style))
  if (Element.getStyle(element,'position') == 'static') value = 'auto';

 return value == 'auto' ? null :value;
},

 setStyle:function(element,style){
 element = $(element);
 for (name in style)
  element.style[name.camelize()] = style[name];
},

 getDimensions:function(element){
 element = $(element);
 if (Element.getStyle(element,'display') != 'none')
  return{width:element.offsetWidth,height:element.offsetHeight};

 // All *Width and *Height properties give 0 on elements with display none,
 // so enable the element temporarily
 var els = element.style;
 var originalVisibility = els.visibility;
 var originalPosition = els.position;
 els.visibility = 'hidden';
 els.position = 'absolute';
 els.display = '';
 var originalWidth = element.clientWidth;
 var originalHeight = element.clientHeight;
 els.display = 'none';
 els.position = originalPosition;
 els.visibility = originalVisibility;
 return{width:originalWidth,height:originalHeight};
},

 makePositioned:function(element){
 element = $(element);
 var pos = Element.getStyle(element,'position');
 if (pos == 'static' || !pos){
  element._madePositioned = true;
  element.style.position = 'relative';
  // Opera returns the offset relative to the positioning context,when an
  // element is position relative but top and left have not been defined
  if (window.opera){
  element.style.top = 0;
  element.style.left = 0;
 }
}
},

 undoPositioned:function(element){
 element = $(element);
 if (element._madePositioned){
  element._madePositioned = undefined;
  element.style.position =
  element.style.top =
  element.style.left =
  element.style.bottom =
  element.style.right = '';
}
},

 makeClipping:function(element){
 element = $(element);
 if (element._overflow) return;
 element._overflow = element.style.overflow;
 if ((Element.getStyle(element,'overflow') || 'visible') != 'hidden')
  element.style.overflow = 'hidden';
},

 undoClipping:function(element){
 element = $(element);
 if (element._overflow) return;
 element.style.overflow = element._overflow;
 element._overflow = undefined;
}
});

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.stripScripts();

 if (this.adjacency && this.element.insertAdjacentHTML){
  try{
  this.element.insertAdjacentHTML(this.adjacency,this.content);
 }catch (e){
  if (this.element.tagName.toLowerCase() == 'tbody'){
   this.insertContent(this.contentFromAnonymousTable());
 }else{
   throw e;
 }
 }
}else{
  this.range = this.element.ownerDocument.createRange();
  if (this.initializeRange) this.initializeRange();
  this.insertContent([this.range.createContextualFragment(this.content)]);
}

 setTimeout(function(){content.evalScripts()},10);
},

 contentFromAnonymousTable:function(){
 var div = document.createElement('div');
 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
 return $A(div.childNodes[0].childNodes[0].childNodes);
}
}

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(fragments){
 fragments.each((function(fragment){
  this.element.parentNode.insertBefore(fragment,this.element);
}).bind(this));
}
});

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(fragments){
 fragments.reverse(false).each((function(fragment){
  this.element.insertBefore(fragment,this.element.firstChild);
}).bind(this));
}
});

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(fragments){
 fragments.each((function(fragment){
  this.element.appendChild(fragment);
}).bind(this));
}
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'),{
 initializeRange:function(){
 this.range.setStartAfter(this.element);
},

 insertContent:function(fragments){
 fragments.each((function(fragment){
  this.element.parentNode.insertBefore(fragment,
  this.element.nextSibling);
}).bind(this));
}
});

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

Element.ClassNames = Class.create();
Element.ClassNames.prototype ={
 initialize:function(element){
 this.element = $(element);
},

 _each:function(iterator){
 this.element.className.split(/\s+/).select(function(name){
  return name.length > 0;
})._each(iterator);
},

 set:function(className){
 this.element.className = className;
},

 add:function(classNameToAdd){
 if (this.include(classNameToAdd)) return;
 this.set(this.toArray().concat(classNameToAdd).join(' '));
},

 remove:function(classNameToRemove){
 if (!this.include(classNameToRemove)) return;
 this.set(this.select(function(className){
  return className != classNameToRemove;
}).join(' '));
},

 toString:function(){
 return this.toArray().join(' ');
}
}

Object.extend(Element.ClassNames.prototype,Enumerable);
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 = $(element);
 element.focus();
 if (element.select)
  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){
 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){
 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 = '';
}
},

 findFirstElement:function(form){
 return Form.getElements(form).find(function(element){
  return element.type != 'hidden' && !element.disabled &&
  ['input','select','textarea'].include(element.tagName.toLowerCase());
});
},

 focusFirstElement:function(form){
 Field.activate(Form.findFirstElement(form));
},

 reset:function(form){
 $(form).reset();
}
}

Form.Element ={
 serialize:function(element){
 element = $(element);
 var method = element.tagName.toLowerCase();
 var parameter = Form.Element.Serializers[method](element);

 if (parameter){
  var key = encodeURIComponent(parameter[0]);
  if (key.length == 0) return;

  if (parameter[1].constructor != Array)
  parameter[1] = [parameter[1]];

  return parameter[1].map(function(value){
  return key + '=' + encodeURIComponent(value);
 }).join('&');
}
},

 getValue:function(element){
 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){
 return Form.Element.Serializers[element.type == 'select-one' ?
  'selectOne' :'selectMany'](element);
},

 selectOne:function(element){
 var value = '',opt,index = element.selectedIndex;
 if (index >= 0){
  opt = element.options[index];
  value = opt.value;
  if (!value && !('value' in opt))
  value = opt.text;
}
 return [element.name,value];
},

 selectMany:function(element){
 var value = new Array();
 for (var i = 0;i < element.length;i++){
  var opt = element.options[i];
  if (opt.selected){
  var optValue = opt.value;
  if (!optValue && !('value' in opt))
   optValue = opt.text;
  value.push(optValue);
 }
}
 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':
   Event.observe(element,'click',this.onElementEvent.bind(this));
   break;
  case 'password':
  case 'text':
  case 'textarea':
  case 'select-one':
  case 'select-multiple':
   Event.observe(element,'change',this.onElementEvent.bind(this));
   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;
  event.cancelBubble = true;
}
},

 // 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.apply(this,Event.observers[i]);
  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.match(/Konqueror|Safari|KHTML/)
  || 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.match(/Konqueror|Safari|KHTML/)
  || element.detachEvent))
  name = 'keydown';

 if (element.removeEventListener){
  element.removeEventListener(name,observer,useCapture);
}else if (element.detachEvent){
  element.detachEvent('on' + name,observer);
}
}
});

/* prevent memory leaks in IE */
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];
},

 positionedOffset:function(element){
 var valueT = 0,valueL = 0;
 do{
  valueT += element.offsetTop || 0;
  valueL += element.offsetLeft || 0;
  element = element.offsetParent;
  if (element){
  p = Element.getStyle(element,'position');
  if (p == 'relative' || p == 'absolute') break;
 }
}while (element);
 return [valueL,valueT];
},

 offsetParent:function(element){
 if (element.offsetParent) return element.offsetParent;
 if (element == document.body) return element;

 while ((element = element.parentNode) && element != document.body)
  if (Element.getStyle(element,'position') != 'static')
  return element;

 return document.body;
},

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

 page:function(forElement){
 var valueT = 0,valueL = 0;

 var element = forElement;
 do{
  valueT += element.offsetTop || 0;
  valueL += element.offsetLeft || 0;

  // Safari fix
  if (element.offsetParent==document.body)
  if (Element.getStyle(element,'position')=='absolute') break;

}while (element = element.offsetParent);

 element = forElement;
 do{
  valueT -= element.scrollTop || 0;
  valueL -= element.scrollLeft || 0;
}while (element = element.parentNode);

 return [valueL,valueT];
},

 clone:function(source,target){
 var options = Object.extend({
  setLeft:true,
  setTop: true,
  setWidth:true,
  setHeight:true,
  offsetTop:0,
  offsetLeft:0
},arguments[2] ||{})

 // find page position of source
 source = $(source);
 var p = Position.page(source);

 // find coordinate system to use
 target = $(target);
 var delta = [0,0];
 var parent = null;
 // delta [0,0] will do fine with position:fixed elements,
 // position:absolute needs offsetParent deltas
 if (Element.getStyle(target,'position') == 'absolute'){
  parent = Position.offsetParent(target);
  delta = Position.page(parent);
}

 // correct by body offsets (fixes Safari)
 if (parent == document.body){
  delta[0] -= document.body.offsetLeft;
  delta[1] -= document.body.offsetTop;
}

 // set position
 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
},

 absolutize:function(element){
 element = $(element);
 if (element.style.position == 'absolute') return;
 Position.prepare();

 var offsets = Position.positionedOffset(element);
 var top  = offsets[1];
 var left = offsets[0];
 var width = element.clientWidth;
 var height = element.clientHeight;

 element._originalLeft = left - parseFloat(element.style.left || 0);
 element._originalTop = top - parseFloat(element.style.top || 0);
 element._originalWidth = element.style.width;
 element._originalHeight = element.style.height;

 element.style.position = 'absolute';
 element.style.top = top + 'px';;
 element.style.left = left + 'px';;
 element.style.width = width + 'px';;
 element.style.height = height + 'px';;
},

 relativize:function(element){
 element = $(element);
 if (element.style.position == 'relative') return;
 Position.prepare();

 element.style.position = 'relative';
 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

 element.style.top = top + 'px';
 element.style.left = left + 'px';
 element.style.height = element._originalHeight;
 element.style.width = element._originalWidth;
}
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned. For performance reasons,redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)){
 Position.cumulativeOffset = function(element){
 var valueT = 0,valueL = 0;
 do{
  valueT += element.offsetTop || 0;
  valueL += element.offsetLeft || 0;
  if (element.offsetParent == document.body)
  if (Element.getStyle(element,'position') == 'absolute') break;

  element = element.offsetParent;
}while (element);

 return [valueL,valueT];
}
}