var Ptyp={
  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(Ptyp.ScriptFragment,'img'),'');
 },
  extractScripts:function(){
    var matchAll=new RegExp(Ptyp.ScriptFragment,'img');
    var matchOne=new RegExp(Ptyp.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||Ptyp.K)(value,index);
      if(!result)throw $break;
   });
    return result;
 },
  any:function(iterator){
    var result=true;
    this.each(function(value,index){
      if(result=!!(iterator||Ptyp.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||Ptyp.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||Ptyp.K)(value,index);
      if(value>=(result||value))
        result=value;
   });
    return result;
 },
  min:function(iterator){
    var result;
    this.each(function(value,index){
      value=(iterator||Ptyp.K)(value,index);
      if(value<=(result||value))
        result=value;
   });
    return result;
 },
  partition:function(iterator){
    var trues=[],falses=[];
    this.each(function(value,index){
     ((iterator||Ptyp.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(Ptyp.K);
 },
  zip:function(){
    var iterator=Ptyp.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-Ptyp-Version',Ptyp.Version];
    if(this.options.method=='post'){
      requestHeaders.push('Content-type',
        'application/x-www-form-urlencoded');
      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')]
        ||Ptyp.emptyFunction)(transport,json);
     }catch(e){
        this.dispatchException(e);
     }
      if((this.header('Content-type')||'').match(/^text\/javascript/i))
        this.evalResponse();
   }
    try{
     (this.options['on'+event]||Ptyp.emptyFunction)(transport,json);
      Ajax.Responders.dispatch('on'+event,this,transport,json);
   }catch(e){
      this.dispatchException(e);
   }
    if(event=='Complete')
      this.transport.onreadystatechange=Ptyp.emptyFunction;
 },
  dispatchException:function(exception){
   (this.options.onException||Ptyp.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||Ptyp.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||Ptyp.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);
 },
  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};
    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';
      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;
   }
 },
  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);
   }
 }});
Event.observe(window,'unload',Event.unloadCache,false);
var Position={
  includeScrollOffsets:false,
  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;
 },
  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);
 },
  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;
      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]||{})
    source=$(source);
    var p=Position.page(source);
    target=$(target);
    var delta=[0,0];
    var parent=null;
    if(Element.getStyle(target,'position')=='absolute'){
      parent=Position.offsetParent(target);
      delta=Position.page(parent);
   }
    if(parent==document.body){
      delta[0]-=document.body.offsetLeft;
      delta[1]-=document.body.offsetTop;
   }
    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;
 }}
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];
 }}
