var Prototype={
  Version:'1.5.1_rc2',

  Browser:{
    IE:   !!(window.attachEvent && !window.opera),
    Opera:!!window.opera,
    WebKit:navigator.userAgent.indexOf('AppleWebKit/')>-1,
    Gecko:navigator.userAgent.indexOf('Gecko')>-1 && navigator.userAgent.indexOf('KHTML')==-1
 },
  BrowserFeatures:{
    XPath:!!document.evaluate,
    ElementExtensions:!!window.HTMLElement,
    SpecificElementExtensions:
     (document.createElement('div').__proto__ !==
       document.createElement('form').__proto__)
 },

  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(var property in source){
    destination[property]=source[property];
 }
  return destination;
}

Object.extend(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;
   }
 },

  toJSON:function(object){
    var type=typeof object;
    switch(type){
      case 'undefined':
      case 'function':
      case 'unknown':return;
      case 'boolean':return object.toString();
   }
    if(object===null) return 'null';
    if(object.toJSON) return object.toJSON();
    if(object.ownerDocument===document) return;
    var results=[];
    for(var property in object){
      var value=Object.toJSON(object[property]);
      if(value !== undefined)
        results.push(property.toJSON()+':'+value);
   }
    return '{'+results.join(',')+'}';
 },

  keys:function(object){
    var keys=[];
    for(var property in object)
      keys.push(property);
    return keys;
 },

  values:function(object){
    var values=[];
    for(var property in object)
      values.push(object[property]);
    return values;
 },

  clone:function(object){
    return Object.extend({},object);
 }
});

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,args=$A(arguments),object=args.shift();
  return function(event){
    return __method.apply(object,[( event || window.event)].concat(args).concat($A(arguments)));
 }
}

Object.extend(Number.prototype,{
  toColorPart:function(){
    return this.toPaddedString(2,16);
 },

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

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

  toPaddedString:function(length,radix){
    var string=this.toString(radix || 10);
    return '0'.times(length - string.length)+string;
 },

  toJSON:function(){
    return isFinite(this)?this.toString() :'null';
 }
});

Date.prototype.toJSON=function(){
  return '"'+this.getFullYear()+'-'+
   (this.getMonth()+1).toPaddedString(2)+'-'+
    this.getDate().toPaddedString(2)+'T'+
    this.getHours().toPaddedString(2)+':'+
    this.getMinutes().toPaddedString(2)+':'+
    this.getSeconds().toPaddedString(2)+'"';
};

var Try={
  these:function(){
    var returnValue;

    for(var i=0,length=arguments.length;i<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(){
    this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency * 1000);
 },

  stop:function(){
    if(!this.timer) return;
    clearInterval(this.timer);
    this.timer=null;
 },

  onTimerEvent:function(){
    if(!this.currentlyExecuting){
      try{
        this.currentlyExecuting=true;
        this.callback(this);
     } finally{
        this.currentlyExecuting=false;
     }
   }
 }
}
Object.extend(String,{
  interpret:function(value){
    return value==null?'' :String(value);
 },
  specialChar:{
    '\b':'\\b',
    '\t':'\\t',
    '\n':'\\n',
    '\f':'\\f',
    '\r':'\\r',
    '\\':'\\\\'
 }
});

Object.extend(String.prototype,{
  gsub:function(pattern,replacement){
    var result='',source=this,match;
    replacement=arguments.callee.prepareReplacement(replacement);

    while(source.length>0){
      if(match=source.match(pattern)){
        result+= source.slice(0,match.index);
        result+= String.interpret(replacement(match));
        source =source.slice(match.index+match[0].length);
     } else{
        result+= source,source='';
     }
   }
    return result;
 },

  sub:function(pattern,replacement,count){
    replacement=this.gsub.prepareReplacement(replacement);
    count=count===undefined?1 :count;

    return this.gsub(pattern,function(match){
      if(--count<0) return match[0];
      return replacement(match);
   });
 },

  scan:function(pattern,iterator){
    this.gsub(pattern,iterator);
    return this;
 },

  truncate:function(length,truncation){
    length=length || 30;
    truncation=truncation===undefined?'...' :truncation;
    return this.length>length ?
      this.slice(0,length - truncation.length)+truncation :this;
 },

  strip:function(){
    return this.replace(/^\s+/,'').replace(/\s+$/,'');
 },

  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(function(script){return eval(script)});
 },

  escapeHTML:function(){
    var self=arguments.callee;
    self.text.data=this;
    return self.div.innerHTML;
 },

  unescapeHTML:function(){
    var div=document.createElement('div');
    div.innerHTML=this.stripTags();
    return div.childNodes[0] ?(div.childNodes.length>1 ?
      $A(div.childNodes).inject('',function(memo,node){return memo+node.nodeValue}) :
      div.childNodes[0].nodeValue) :'';
 },

  toQueryParams:function(separator){
    var match=this.strip().match(/([^?#]*)(#.*)?$/);
    if(!match) return{};

    return match[1].split(separator || '&').inject({},function(hash,pair){
      if((pair=pair.split('='))[0]){
        var name=decodeURIComponent(pair[0]);
        var value=pair[1]?decodeURIComponent(pair[1]) :undefined;

        if(hash[name] !== undefined){
          if(hash[name].constructor != Array)
            hash[name]=[hash[name]];
          if(value) hash[name].push(value);
       }
        else hash[name]=value;
     }
      return hash;
   });
 },

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

  succ:function(){
    return this.slice(0,this.length - 1)+
      String.fromCharCode(this.charCodeAt(this.length - 1)+1);
 },

  times:function(count){
    var result='';
    for(var i=0;i<count;i++) result+= this;
    return result;
 },

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

    var camelized=this.charAt(0)=='-'
     ?parts[0].charAt(0).toUpperCase()+parts[0].substring(1)
      :parts[0];

    for(var i=1;i<len;i++)
      camelized+= parts[i].charAt(0).toUpperCase()+parts[i].substring(1);

    return camelized;
 },

  capitalize:function(){
    return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase();
 },

  underscore:function(){
    return this.gsub(/::/,'/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
 },

  dasherize:function(){
    return this.gsub(/_/,'-');
 },

  inspect:function(useDoubleQuotes){
    var escapedString=this.gsub(/[\x00-\x1f\\]/,function(match){
      var character=String.specialChar[match[0]];
      return character?character :'\\u00'+match[0].charCodeAt().toPaddedString(2,16);
   });
    if(useDoubleQuotes) return '"'+escapedString.replace(/"/g,'\\"')+'"';
    return "'"+escapedString.replace(/'/g,'\\\'')+"'";
 },

  toJSON:function(){
    return this.inspect(true);
 },

  evalJSON:function(sanitize){
    try{
      if(!sanitize ||(/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)))
        return eval('('+this+')');
   } catch(e){}
    throw new SyntaxError('Badly formated JSON string:'+this.inspect());
 },

  include:function(pattern){
    return this.indexOf(pattern)>-1;
 },

  startsWith:function(pattern){
    return this.indexOf(pattern)==0;
 },

  endsWith:function(pattern){
    return this.lastIndexOf(pattern)==(this.length - pattern.length);
 },

  empty:function(){
    return this=='';
 },

  blank:function(){
    return /^\s*$/.test(this);
 }
});

String.prototype.gsub.prepareReplacement=function(replacement){
  if(typeof replacement=='function') return replacement;
  var template=new Template(replacement);
  return function(match){return template.evaluate(match)};
}

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

Object.extend(String.prototype.escapeHTML,{
  div:document.createElement('div'),
  text:document.createTextNode('')
});

with(String.prototype.escapeHTML) div.appendChild(text);

var Template=Class.create();
Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype={
  initialize:function(template,pattern){
    this.template=template.toString();
    this.pattern =pattern || Template.Pattern;
 },

  evaluate:function(object){
    return this.template.gsub(this.pattern,function(match){
      var before=match[1];
      if(before=='\\') return match[2];
      return before+String.interpret(object[match[3]]);
   });
 }
}

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

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

  eachSlice:function(number,iterator){
    var index=-number,slices=[],array=this.toArray();
    while((index+= number)<array.length)
      slices.push(array.slice(index,index+number));
    return slices.map(iterator);
 },

  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=false;
    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 || Prototype.K)(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;
 },

  inGroupsOf:function(number,fillWith){
    fillWith=fillWith===undefined?null :fillWith;
    return this.eachSlice(number,function(slice){
      while(slice.length<number) slice.push(fillWith);
      return slice;
   });
 },

  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.map(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(result==undefined || value >= result)
        result=value;
   });
    return result;
 },

  min:function(iterator){
    var result;
    this.each(function(value,index){
      value=(iterator || Prototype.K)(value,index);
      if(result==undefined || value<result)
        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.map(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.map();
 },

  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){
      return iterator(collections.pluck(index));
   });
 },

  size:function(){
    return this.toArray().length;
 },

  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,length=iterable.length;i<length;i++)
      results.push(iterable[i]);
    return results;
 }
}

if(Prototype.Browser.WebKit){
  $A=Array.from=function(iterable){
    if(!iterable) return [];
    if(!(typeof iterable=='function' && iterable=='[object NodeList]') &&
      iterable.toArray){
      return iterable.toArray();
   } else{
      var results=[];
      for(var i=0,length=iterable.length;i<length;i++)
        results.push(iterable[i]);
      return results;
   }
 }
}

Object.extend(Array.prototype,Enumerable);

if(!Array.prototype._reverse)
  Array.prototype._reverse=Array.prototype.reverse;

Object.extend(Array.prototype,{
  _each:function(iterator){
    for(var i=0,length=this.length;i<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 != null;
   });
 },

  flatten:function(){
    return this.inject([],function(array,value){
      return array.concat(value && 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,length=this.length;i<length;i++)
      if(this[i]==object) return i;
    return -1;
 },

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

  reduce:function(){
    return this.length>1?this :this[0];
 },

  uniq:function(sorted){
    return this.inject([],function(array,value,index){
      if(0==index ||(sorted?array.last() != value :!array.include(value)))
        array.push(value);
      return array;
   });
 },

  clone:function(){
    return [].concat(this);
 },

  size:function(){
    return this.length;
 },

  inspect:function(){
    return '['+this.map(Object.inspect).join(',')+']';
 },

  toJSON:function(){
    var results=[];
    this.each(function(object){
      var value=Object.toJSON(object);
      if(value !== undefined) results.push(value);
   });
    return '['+results.join(',')+']';
 }
});

Array.prototype.toArray=Array.prototype.clone;

function $w(string){
  string=string.strip();
  return string?string.split(/\s+/) :[];
}

if(Prototype.Browser.Opera){
  Array.prototype.concat=function(){
    var array=[];
    for(var i=0,length=this.length;i<length;i++) array.push(this[i]);
    for(var i=0,length=arguments.length;i<length;i++){
      if(arguments[i].constructor==Array){
        for(var j=0,arrayLength=arguments[i].length;j<arrayLength;j++)
          array.push(arguments[i][j]);
     } else{
        array.push(arguments[i]);
     }
   }
    return array;
 }
}
var Hash=function(object){
  if(object instanceof Hash) this.merge(object);
  else Object.extend(this,object ||{});
};

Object.extend(Hash,{
  toQueryString:function(obj){
    var parts=[];
    parts.add=arguments.callee.addPair;

    this.prototype._each.call(obj,function(pair){
      if(!pair.key) return;
      var value=pair.value;

      if(value && typeof value=='object'){
        if(value.constructor==Array) value.each(function(value){
          parts.add(pair.key,value);
       });
        return;
     }
      parts.add(pair.key,value);
   });

    return parts.join('&');
 },

  toJSON:function(object){
    var results=[];
    this.prototype._each.call(object,function(pair){
      var value=Object.toJSON(pair.value);
      if(value !== undefined) results.push(pair.key.toJSON()+':'+value);
   });
    return '{'+results.join(',')+'}';
 }
});

Hash.toQueryString.addPair=function(key,value,prefix){
  if(value==null) return;
  key=encodeURIComponent(key);
  this.push(key+'='+(value==null?'' :encodeURIComponent(value)));
}

Object.extend(Hash.prototype,Enumerable);
Object.extend(Hash.prototype,{
  _each:function(iterator){
    for(var key in this){
      var value=this[key];
      if(value && value==Hash.prototype[key]) 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(this,function(mergedHash,pair){
      mergedHash[pair.key]=pair.value;
      return mergedHash;
   });
 },

  remove:function(){
    var result;
    for(var i=0,length=arguments.length;i<length;i++){
      var value=this[arguments[i]];
      if(value !== undefined){
        if(result===undefined) result=value;
        else{
          if(result.constructor != Array) result=[result];
          result.push(value)
       }
     }
      delete this[arguments[i]];
   }
    return result;
 },

  toQueryString:function(){
    return Hash.toQueryString(this);
 },

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

  toJSON:function(){
    return Hash.toJSON(this);
 }
});

function $H(object){
  if(object instanceof Hash) return object;
  return new Hash(object);
};

// Safari iterates over shadowed properties
if(function(){
  var i=0,Test=function(value){this.key=value};
  Test.prototype.key='foo';
  for(var property in new Test('bar')) i++;
  return i>1;
}()) Hash.prototype._each=function(iterator){
  var cache=[];
  for(var key in this){
    var value=this[key];
    if((value && value==Hash.prototype[key]) || cache.include(key)) continue;
    cache.push(key);
    var pair=[key,value];
    pair.key=key;
    pair.value=value;
    iterator(pair);
 }
};
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;
    while(this.include(value)){
      iterator(value);
      value=value.succ();
   }
 },

  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 XMLHttpRequest()},
      function(){return new ActiveXObject('Msxml2.XMLHTTP')},
      function(){return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
 },

  activeRequestCount:0
}

Ajax.Responders={
  responders:[],

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

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

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

  dispatch:function(callback,request,transport,json){
    this.each(function(responder){
      if(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,
      contentType:'application/x-www-form-urlencoded',
      encoding:   'UTF-8',
      parameters: ''
   }
    Object.extend(this.options,options ||{});

    this.options.method=this.options.method.toLowerCase();
    if(typeof this.options.parameters=='string')
      this.options.parameters=this.options.parameters.toQueryParams();
 }
}

Ajax.Request=Class.create();
Ajax.Request.Events =
  ['Uninitialized','Loading','Loaded','Interactive','Complete'];

Ajax.Request.prototype=Object.extend(new Ajax.Base(),{
  _complete:false,

  initialize:function(url,options){
    this.transport=Ajax.getTransport();
    this.setOptions(options);
    this.request(url);
 },

  request:function(url){
    this.url=url;
    this.method=this.options.method;
    var params=Object.clone(this.options.parameters);

    if(!['get','post'].include(this.method)){
      // simulate other verbs over post
      params['_method']=this.method;
      this.method='post';
   }

    this.parameters=params;

    if(params=Hash.toQueryString(params)){
      // when GET,append parameters to URL
      if(this.method=='get')
        this.url+=(this.url.include('?')?'&' :'?')+params;
      else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params+= '&_=';
   }

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

      this.transport.open(this.method.toUpperCase(),this.url,
        this.options.asynchronous);

      if(this.options.asynchronous)
        setTimeout(function(){this.respondToReadyState(1)}.bind(this),10);

      this.transport.onreadystatechange=this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body=this.method=='post' ?(this.options.postBody || params) :null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if(!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

   }
    catch(e){
      this.dispatchException(e);
   }
 },

  onStateChange:function(){
    var readyState=this.transport.readyState;
    if(readyState>1 && !((readyState==4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
 },

  setRequestHeaders:function(){
    var headers={
      'X-Requested-With':'XMLHttpRequest',
      'X-Prototype-Version':Prototype.Version,
      'Accept':'text/javascript,text/html,application/xml,text/xml,*/*'
   };

    if(this.method=='post'){
      headers['Content-type']=this.options.contentType+
       (this.options.encoding?';charset='+this.options.encoding :'');

      /* Force "Connection:close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if(this.transport.overrideMimeType &&
         (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1]<2005)
            headers['Connection']='close';
   }

    // user-defined headers
    if(typeof this.options.requestHeaders=='object'){
      var extras=this.options.requestHeaders;

      if(typeof extras.push=='function')
        for(var i=0,length=extras.length;i<length;i+= 2)
          headers[extras[i]]=extras[i+1];
      else
        $H(extras).each(function(pair){headers[pair.key]=pair.value});
   }

    for(var name in headers)
      this.transport.setRequestHeader(name,headers[name]);
 },

  success:function(){
    return !this.transport.status
        ||(this.transport.status >= 200 && this.transport.status<300);
 },

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

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

      if((this.getHeader('Content-type') || 'text/javascript').strip().
        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
          this.evalResponse();
   }

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

    if(state=='Complete'){
      // avoid memory leak in MSIE:clean up
      this.transport.onreadystatechange=Prototype.emptyFunction;
   }
 },

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

  evalJSON:function(){
    try{
      var json=this.getHeader('X-JSON');
      return json?eval('('+json+')') :null;
   } catch(e){return null}
 },

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

  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.container={
      success:(container.success || 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,param){
      this.updateContent();
      onComplete(transport,param);
   }).bind(this);

    this.request(url);
 },

  updateContent:function(){
    var receiver=this.container[this.success()?'success' :'failure'];
    var response=this.transport.responseText;

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

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

    if(this.success()){
      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.options.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);
 }
});
function $(element){
  if(arguments.length>1){
    for(var i=0,elements=[],length=arguments.length;i<length;i++)
      elements.push($(arguments[i]));
    return elements;
 }
  if(typeof element=='string')
    element=document.getElementById(element);
  return Element.extend(element);
}

if(Prototype.BrowserFeatures.XPath){
  document._getElementsByXPath=function(expression,parentElement){
    var results=[];
    var query=document.evaluate(expression,$(parentElement) || document,
      null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
    for(var i=0,length=query.snapshotLength;i<length;i++)
      results.push(query.snapshotItem(i));
    return results;
 };

  document.getElementsByClassName=function(className,parentElement){
    var q=".//*[contains(concat(' ',@class,' '),' "+className+" ')]";
    return document._getElementsByXPath(q,parentElement);
 }

} else document.getElementsByClassName=function(className,parentElement){
  var children=($(parentElement) || document.body).getElementsByTagName('*');
  var elements=[],child;
  for(var i=0,length=children.length;i<length;i++){
    child=children[i];
    if(Element.hasClassName(child,className))
      elements.push(Element.extend(child));
 }
  return elements;
};

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

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

Element.extend=function(element){
  var F=Prototype.BrowserFeatures;
  if(!element || !element.tagName || element.nodeType==3 ||
   element._extended || F.SpecificElementExtensions || element==window)
    return element;

  var methods={},tagName=element.tagName,cache=Element.extend.cache,
   T=Element.Methods.ByTag;

  // extend methods for all tags(Safari doesn't need this)
  if(!F.ElementExtensions){
    Object.extend(methods,Element.Methods),
    Object.extend(methods,Element.Methods.Simulated);
 }

  // extend methods for specific tags
  if(T[tagName]) Object.extend(methods,T[tagName]);

  for(var property in methods){
    var value=methods[property];
    if(typeof value=='function' && !(property in element))
      element[property]=cache.findOrStore(value);
 }

  element._extended=Prototype.emptyFunction;
  return element;
};

Element.extend.cache={
  findOrStore:function(value){
    return this[value]=this[value] || function(){
      return value.apply(null,[this].concat($A(arguments)));
   }
 }
};

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

  toggle:function(element){
    element=$(element);
    Element[Element.visible(element)?'hide' :'show'](element);
    return element;
 },

  hide:function(element){
    $(element).style.display='none';
    return element;
 },

  show:function(element){
    $(element).style.display='';
    return element;
 },

  remove:function(element){
    element=$(element);
    element.parentNode.removeChild(element);
    return element;
 },

  update:function(element,html){
    html=typeof html=='undefined'?'' :html.toString();
    $(element).innerHTML=html.stripScripts();
    setTimeout(function(){html.evalScripts()},10);
    return element;
 },

  replace:function(element,html){
    element=$(element);
    html=typeof html=='undefined'?'' :html.toString();
    if(element.outerHTML){
      element.outerHTML=html.stripScripts();
   } else{
      var range=element.ownerDocument.createRange();
      range.selectNodeContents(element);
      element.parentNode.replaceChild(
        range.createContextualFragment(html.stripScripts()),element);
   }
    setTimeout(function(){html.evalScripts()},10);
    return element;
 },

  inspect:function(element){
    element=$(element);
    var result='<'+element.tagName.toLowerCase();
    $H({'id':'id','className':'class'}).each(function(pair){
      var property=pair.first(),attribute=pair.last();
      var value=(element[property] || '').toString();
      if(value) result+= ' '+attribute+'='+value.inspect(true);
   });
    return result+'>';
 },

  recursivelyCollect:function(element,property){
    element=$(element);
    var elements=[];
    while(element=element[property])
      if(element.nodeType==1)
        elements.push(Element.extend(element));
    return elements;
 },

  ancestors:function(element){
    return $(element).recursivelyCollect('parentNode');
 },

  descendants:function(element){
    return $A($(element).getElementsByTagName('*')).each(Element.extend);
 },

  immediateDescendants:function(element){
    if(!(element=$(element).firstChild)) return [];
    while(element && element.nodeType != 1) element=element.nextSibling;
    if(element) return [element].concat($(element).nextSiblings());
    return [];
 },

  previousSiblings:function(element){
    return $(element).recursivelyCollect('previousSibling');
 },

  nextSiblings:function(element){
    return $(element).recursivelyCollect('nextSibling');
 },

  siblings:function(element){
    element=$(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
 },

  match:function(element,selector){
    if(typeof selector=='string')
      selector=new Selector(selector);
    return selector.match($(element));
 },

  up:function(element,expression,index){
    var ancestors=$(element).ancestors();
    return expression?Selector.findElement(ancestors,expression,index) :
      ancestors[index || 0];
 },

  down:function(element,expression,index){
    var descendants=$(element).descendants();
    return expression?Selector.findElement(descendants,expression,index) :
      descendants[index || 0];
 },

  previous:function(element,expression,index){
    var previousSiblings=$(element).previousSiblings();
    return expression?Selector.findElement(previousSiblings,expression,index) :
      previousSiblings[index || 0];
 },

  next:function(element,expression,index){
    var nextSiblings=$(element).nextSiblings();
    return expression?Selector.findElement(nextSiblings,expression,index) :
      nextSiblings[index || 0];
 },

  getElementsBySelector:function(){
    var args=$A(arguments),element=$(args.shift());
    return Selector.findChildElements(element,args);
 },

  getElementsByClassName:function(element,className){
    return document.getElementsByClassName(className,element);
 },

  readAttribute:function(element,name){
    element=$(element);
    if(Prototype.Browser.IE){
      if(!element.attributes) return null;
      var t=Element._attributeTranslations;
      if(t.values[name]) return t.values[name](element,name);
      if(t.names[name])  name=t.names[name];
      var attribute=element.attributes[name];
      return attribute?attribute.nodeValue :null;
   }
    return element.getAttribute(name);
 },

  getHeight:function(element){
    return $(element).getDimensions().height;
 },

  getWidth:function(element){
    return $(element).getDimensions().width;
 },

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

  hasClassName:function(element,className){
    if(!(element=$(element))) return;
    var elementClassName=element.className;
    if(elementClassName.length==0) return false;
    if(elementClassName==className ||
        elementClassName.match(new RegExp("(^|\\s)"+className+"(\\s|$)")))
      return true;
    return false;
 },

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

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

  toggleClassName:function(element,className){
    if(!(element=$(element))) return;
    Element.classNames(element)[element.hasClassName(className)?'remove' :'add'](className);
    return element;
 },

  observe:function(){
    Event.observe.apply(Event,arguments);
    return $A(arguments).first();
 },

  stopObserving:function(){
    Event.stopObserving.apply(Event,arguments);
    return $A(arguments).first();
 },

  // removes whitespace-only text node children
  cleanWhitespace:function(element){
    element=$(element);
    var node=element.firstChild;
    while(node){
      var nextNode=node.nextSibling;
      if(node.nodeType==3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node=nextNode;
   }
    return element;
 },

  empty:function(element){
    return $(element).innerHTML.blank();
 },

  descendantOf:function(element,ancestor){
    element=$(element),ancestor=$(ancestor);
    while(element=element.parentNode)
      if(element==ancestor) return true;
    return false;
 },

  scrollTo:function(element){
    element=$(element);
    var pos=Position.cumulativeOffset(element);
    window.scrollTo(pos[0],pos[1]);
    return element;
 },

  getStyle:function(element,style){
    element=$(element);
    style=style=='float'?'cssFloat' :style.camelize();
    var value=element.style[style];
    if(!value){
      var css=document.defaultView.getComputedStyle(element,null);
      value=css?css[style] :null;
   }
    if(style=='opacity') return value?parseFloat(value) :1.0;
    return value=='auto'?null :value;
 },

  getOpacity:function(element){
    return $(element).getStyle('opacity');
 },

  setStyle:function(element,styles,camelized){
    element=$(element);
    var elementStyle=element.style;

    for(var property in styles)
      if(property=='opacity') element.setOpacity(styles[property])
      else
        elementStyle[(property=='float' || property=='cssFloat') ?
         (elementStyle.styleFloat===undefined?'cssFloat' :'styleFloat') :
         (camelized?property :property.camelize())]=styles[property];

    return element;
 },

  setOpacity:function(element,value){
    element=$(element);
    element.style.opacity=(value==1 || value==='')?'' :
     (value<0.00001)?0 :value;
    return element;
 },

  getDimensions:function(element){
    element=$(element);
    var display=$(element).getStyle('display');
    if(display != 'none' && display != null) // Safari bug
      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;
    var originalDisplay=els.display;
    els.visibility='hidden';
    els.position='absolute';
    els.display='block';
    var originalWidth=element.clientWidth;
    var originalHeight=element.clientHeight;
    els.display=originalDisplay;
    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;
     }
   }
    return element;
 },

  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='';
   }
    return element;
 },

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

  undoClipping:function(element){
    element=$(element);
    if(!element._overflow) return element;
    element.style.overflow=element._overflow=='auto'?'' :element._overflow;
    element._overflow=null;
    return element;
 }
};

Object.extend(Element.Methods,{childOf:Element.Methods.descendantOf});

if(Prototype.Browser.Opera){
  Element.Methods._getStyle=Element.Methods.getStyle;
  Element.Methods.getStyle=function(element,style){
    switch(style){
      case 'left':
      case 'top':
      case 'right':
      case 'bottom':
        if(Element._getStyle(element,'position')=='static') return null;
      default:return Element._getStyle(element,style);
   }
 };
}
else if(Prototype.Browser.IE){
  Element.Methods.getStyle=function(element,style){
    element=$(element);
    style=(style=='float' || style=='cssFloat')?'styleFloat' :style.camelize();
    var value=element.style[style];
    if(!value && element.currentStyle) value=element.currentStyle[style];

    if(style=='opacity'){
      if(value=(element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if(value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
   }

    if(value=='auto'){
      if((style=='width' || style=='height') &&(element.getStyle('display') != 'none'))
        return element['offset'+style.capitalize()]+'px';
      return null;
   }
    return value;
 };

  Element.Methods.setOpacity=function(element,value){
    element=$(element);
    var filter=element.getStyle('filter'),style=element.style;
    if(value==1 || value===''){
      style.filter=filter.replace(/alpha\([^\)]*\)/gi,'');
      return element;
   } else if(value<0.00001) value=0;
    style.filter=filter.replace(/alpha\([^\)]*\)/gi,'')+
      'alpha(opacity='+(value * 100)+')';
    return element;
 };

  // IE is missing .innerHTML support for TABLE-related elements
  Element.Methods.update=function(element,html){
    element=$(element);
    html=typeof html=='undefined'?'' :html.toString();
    var tagName=element.tagName.toUpperCase();
    if(['THEAD','TBODY','TR','TD'].include(tagName)){
      var div=document.createElement('div');
      switch(tagName){
        case 'THEAD':
        case 'TBODY':
          div.innerHTML='<table><tbody>'+ html.stripScripts()+'</tbody></table>';
          depth=2;
          break;
        case 'TR':
          div.innerHTML='<table><tbody><tr>'+ html.stripScripts()+'</tr></tbody></table>';
          depth=3;
          break;
        case 'TD':
          div.innerHTML='<table><tbody><tr><td>'+ html.stripScripts()+'</td></tr></tbody></table>';
          depth=4;
     }
      $A(element.childNodes).each(function(node){element.removeChild(node)});
      depth.times(function(){div=div.firstChild});
      $A(div.childNodes).each(function(node){element.appendChild(node)});
   } else{
      element.innerHTML=html.stripScripts();
   }
    setTimeout(function(){html.evalScripts()},10);
    return element;
 }
}
else if(Prototype.Browser.Gecko){
  Element.Methods.setOpacity=function(element,value){
    element=$(element);
    element.style.opacity=(value==1)?0.999999 :
     (value==='')?'' :(value<0.00001)?0 :value;
    return element;
 };
}

Element._attributeTranslations={
  names:{
    colspan: "colSpan",
    rowspan: "rowSpan",
    valign:  "vAlign",
    datetime:"dateTime",
    accesskey:"accessKey",
    tabindex:"tabIndex",
    enctype: "encType",
    maxlength:"maxLength",
    readonly:"readOnly",
    longdesc:"longDesc"
 },
  values:{
    _getAttr:function(element,attribute){
      return element.getAttribute(attribute,2);
   },
    _flag:function(element,attribute){
      return $(element).hasAttribute(attribute)?attribute :null;
   },
    style:function(element){
      return element.style.cssText.toLowerCase();
   },
    title:function(element){
      var node=element.getAttributeNode('title');
      return node.specified?node.nodeValue :null;
   }
 }
};

(function(){
  Object.extend(this,{
    href:this._getAttr,
    src:this._getAttr,
    disabled:this._flag,
    checked:this._flag,
    readonly:this._flag,
    multiple:this._flag
 });
}).call(Element._attributeTranslations.values);

Element.Methods.Simulated={
  hasAttribute:function(element,attribute){
    var t=Element._attributeTranslations,node;
    attribute=t.names[attribute] || attribute;
    node=$(element).getAttributeNode(attribute);
    return node && node.specified;
 }
};

Element.Methods.ByTag={};

Object.extend(Element,Element.Methods);

if(!Prototype.BrowserFeatures.ElementExtensions &&
 document.createElement('div').__proto__){
  window.HTMLElement={};
  window.HTMLElement.prototype=document.createElement('div').__proto__;
  Prototype.BrowserFeatures.ElementExtensions=true;
}

Element.hasAttribute=function(element,attribute){
  if(element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element,attribute);
};

Element.addMethods=function(methods){
  var F=Prototype.BrowserFeatures,T=Element.Methods.ByTag;
  if(arguments.length==2){
    var tagName=methods;
    methods=arguments[1];
 }

  if(!tagName) Object.extend(Element.Methods,methods ||{});
  else{
    if(tagName.constructor==Array) tagName.each(extend);
    else extend(tagName);
 }

  function extend(tagName){
    tagName=tagName.toUpperCase();
    if(!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName]={};
    Object.extend(Element.Methods.ByTag[tagName],methods);
 }

  function copy(methods,destination,onlyIfAbsent){
    onlyIfAbsent=onlyIfAbsent || false;
    var cache=Element.extend.cache;
    for(var property in methods){
      var value=methods[property];
      if(!onlyIfAbsent || !(property in destination))
        destination[property]=cache.findOrStore(value);
   }
 }

  function findDOMClass(tagName){
    var klass;
    var trans={
      "OPTGROUP":"OptGroup","TEXTAREA":"TextArea","P":"Paragraph",
      "FIELDSET":"FieldSet","UL":"UList","OL":"OList","DL":"DList",
      "DIR":"Directory","H1":"Heading","H2":"Heading","H3":"Heading",
      "H4":"Heading","H5":"Heading","H6":"Heading","Q":"Quote",
      "INS":"Mod","DEL":"Mod","A":"Anchor","IMG":"Image","CAPTION":
      "TableCaption","COL":"TableCol","COLGROUP":"TableCol","THEAD":
      "TableSection","TFOOT":"TableSection","TBODY":"TableSection","TR":
      "TableRow","TH":"TableCell","TD":"TableCell","FRAMESET":
      "FrameSet","IFRAME":"IFrame"
   };
    if(trans[tagName]) klass='HTML'+trans[tagName]+'Element';
    if(window[klass]) return window[klass];
    klass='HTML'+tagName+'Element';
    if(window[klass]) return window[klass];
    klass='HTML'+tagName.capitalize()+'Element';
    if(window[klass]) return window[klass];

    window[klass]={};
    window[klass].prototype=document.createElement(tagName).__proto__;
    return window[klass];
 }

  if(F.ElementExtensions){
    copy(Element.Methods,HTMLElement.prototype);
    copy(Element.Methods.Simulated,HTMLElement.prototype,true);
 }

  if(F.SpecificElementExtensions){
    for(var tag in Element.Methods.ByTag){
      var klass=findDOMClass(tag);
      if(typeof klass=="undefined") continue;
      copy(T[tag],klass.prototype);
   }
 }
};

var 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){
        var tagName=this.element.tagName.toUpperCase();
        if(['TBODY','TR'].include(tagName)){
          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($A(this).concat(classNameToAdd).join(' '));
 },

  remove:function(classNameToRemove){
    if(!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
 },

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

Object.extend(Element.ClassNames.prototype,Enumerable);
/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
 * part of YUI-Ext version 0.40,distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector=Class.create();

Selector.prototype={
  initialize:function(expression){
    this.expression=expression.strip();
    this.compileMatcher();
 },

  compileMatcher:function(){
    // Selectors with namespaced attributes can't use the XPath version
    if(Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
      return this.compileXPathMatcher();

    var e=this.expression,ps=Selector.patterns,h=Selector.handlers,
        c=Selector.criteria,le,p,m;

    if(Selector._cache[e]){
      this.matcher=Selector._cache[e];return;
   }
    this.matcher=["this.matcher=function(root){",
                    "var r=root,h=Selector.handlers,c=false,n;"];

    while(e && le != e &&(/\S/).test(e)){
      le=e;
      for(var i in ps){
        p=ps[i];
        if(m=e.match(p)){
          this.matcher.push(typeof c[i]=='function'?c[i](m) :
    	      new Template(c[i]).evaluate(m));
          e=e.replace(m[0],'');
          break;
       }
     }
   }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression]=this.matcher;
 },

  compileXPathMatcher:function(){
    var e=this.expression,ps=Selector.patterns,
        x=Selector.xpath,le,p,m;

    if(Selector._cache[e]){
      this.xpath=Selector._cache[e];return;
   }

    this.matcher=['.//*'];
    while(e && le != e &&(/\S/).test(e)){
      le=e;
      for(var i in ps){
        if(m=e.match(ps[i])){
          this.matcher.push(typeof x[i]=='function'?x[i](m) :
            new Template(x[i]).evaluate(m));
          e=e.replace(m[0],'');
          break;
       }
     }
   }

    this.xpath=this.matcher.join('');
    Selector._cache[this.expression]=this.xpath;
 },

  findElements:function(root){
    root=root || document;
    if(this.xpath) return document._getElementsByXPath(this.xpath,root);
    return this.matcher(root);
 },

  match:function(element){
    return this.findElements(document).include(element);
 },

  toString:function(){
    return this.expression;
 },

  inspect:function(){
    return "#<Selector:"+this.expression.inspect()+">";
 }
};

Object.extend(Selector,{
  _cache:{},

  xpath:{
    descendant: "//*",
    child:      "/*",
    adjacent:   "/following-sibling::*[1]",
    laterSibling:'/following-sibling::*',
    tagName:    function(m){
      if(m[1]=='*') return '';
      return "[local-name()='"+m[1].toLowerCase()+
             "' or local-name()='"+m[1].toUpperCase()+"']";
   },
    className:  "[contains(concat(' ',@class,' '),' #{1} ')]",
    id:         "[@id='#{1}']",
    attrPresence:"[@#{1}]",
    attr:function(m){
      m[3]=m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
   },
    pseudo:function(m){
      var h=Selector.xpath.pseudos[m[1]];
      if(!h) return '';
      if(typeof h==='function') return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
   },
    operators:{
      '=':"[@#{1}='#{3}']",
      '!=':"[@#{1}!='#{3}']",
      '^=':"[starts-with(@#{1},'#{3}')]",
      '$=':"[substring(@#{1},(string-length(@#{1}) - string-length('#{3}')+1))='#{3}']",
      '*=':"[contains(@#{1},'#{3}')]",
      '~=':"[contains(concat(' ',@#{1},' '),' #{3} ')]",
      '|=':"[contains(concat('-',@#{1},'-'),'-#{3}-')]"
   },
    pseudos:{
      'first-child':'[not(preceding-sibling::*)]',
      'last-child':'[not(following-sibling::*)]',
      'only-child':'[not(preceding-sibling::* or following-sibling::*)]',
      'empty':     "[count(*)=0 and(count(text())=0 or translate(text(),' \t\r\n','')='')]",
      'checked':   "[@checked]",
      'disabled':  "[@disabled]",
      'enabled':   "[not(@disabled)]",
      'not':function(m){
        if(!m[6]) return '';
        var p=Selector.patterns,x=Selector.xpath;
        for(var i in p){
          if(mm=m[6].match(p[i])){
            var ss=typeof x[i]=='function'?x[i](mm) :new Template(x[i]).evaluate(mm);
            m[6]=ss.substring(1,ss.length - 1);
            break;
         }
       }
        return "[not("+m[6]+")]";
     },
      'nth-child':    function(m){
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*)+1) ",m);
     },
      'nth-last-child':function(m){
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*)+1) ",m);
     },
      'nth-of-type':  function(m){
        return Selector.xpath.pseudos.nth("position() ",m);
     },
      'nth-last-of-type':function(m){
        return Selector.xpath.pseudos.nth("(last()+1 - position()) ",m);
     },
      'first-of-type':function(m){
        m[6]="1";return Selector.xpath.pseudos['nth-of-type'](m);
     },
      'last-of-type': function(m){
        m[6]="1";return Selector.xpath.pseudos['nth-last-of-type'](m);
     },
      'only-of-type': function(m){
        var p=Selector.xpath.pseudos;return p['first-of-type'](m)+p['last-of-type'](m);
     },
      nth:function(predicate,m){
        var mm,formula=m[6];
        if(formula=='even') formula='2n+0';
        if(formula=='odd')  formula='2n+1';
        if(mm=formula.match(/^(\d+)$/)) // digit only
          predicate+= "= "+mm[1];
        if(mm=formula.match(/^(\d+)?n(\+(\d+))?/)){// an+b
          var a=mm[1]?Number(mm[1]) :1;
          var b=mm[3]?Number(mm[3]) :0;
          predicate+= "mod "+a+"="+b;
       }
        return "["+predicate+"]";
     }
   }
 },

  criteria:{
    tagName:    'n=h.tagName(n,r,"#{1}",c);  c=false;',
    className:  'n=h.className(n,r,"#{1}",c);c=false;',
    id:         'n=h.id(n,r,"#{1}",c);       c=false;',
    attrPresence:'n=h.attrPresence(n,r,"#{1}");c=false;',
    attr:function(m){
      m[3]=m[5] || m[6];
      return new Template('n=h.attr(n,r,"#{1}","#{3}","#{2}");c=false;').evaluate(m);
   },
    pseudo:     'n=h.pseudo(n,"#{1}","#{6}",r,c);c=false;',
    descendant: 'c="descendant";',
    child:      'c="child";',
    adjacent:   'c="adjacent";',
    laterSibling:'c="laterSibling";'
 },

  patterns:{
    // combinators must be listed first
    //(and descendant needs to be last combinator)
    laterSibling:/^\s*~\s*/,
    child:      /^\s*>\s*/,
    adjacent:   /^\s*\+\s*/,
    descendant: /^\s/,

    // selectors follow
    tagName:    /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:         /^#([\w\-\*]+)(\b|$)/,
    className:  /^\.([\w\-\*]+)(\b|$)/,
    pseudo:     /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/,
    attrPresence:/^\[([\w]+)\]/,
    attr:       /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
 },

  handlers:{
    // UTILITY FUNCTIONS
    // joins two collections
    concat:function(a,b){
      for(var i=0,node;node=b[i];i++)
        a.push(node);
      return a;
   },

    // marks an array of nodes for counting
    mark:function(nodes){
      for(var i=0,node;node=nodes[i];i++)
        node._counted=true;
      return nodes;
   },

    unmark:function(nodes){
      for(var i=0,node;node=nodes[i];i++)
        node._counted=undefined;
      return nodes;
   },

    // mark each child node with its position(for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index:function(parentNode,reverse,ofType){
      parentNode._counted=true;
      if(reverse){
        for(var nodes=parentNode.childNodes,i=nodes.length - 1,j=1;i >= 0;i--){
          node=nodes[i];
          if(node.nodeType==1 &&(!ofType || node._counted)) node.nodeIndex=j++;
       }
     } else{
        for(var i=0,j=1,nodes=parentNode.childNodes;node=nodes[i];i++)
          if(node.nodeType==1 &&(!ofType || node._counted)) node.nodeIndex=j++;
     }
   },

    // filters out duplicates and extends all nodes
    unique:function(nodes){
      if(nodes.length==0) return nodes;
      var results=[],n;
      for(var i=0,l=nodes.length;i<l;i++)
        if(!(n=nodes[i])._counted){
          n._counted=true;
          results.push(Element.extend(n));
       }
      return Selector.handlers.unmark(results);
   },

    // COMBINATOR FUNCTIONS
    descendant:function(nodes){
      var h=Selector.handlers;
      for(var i=0,results=[],node;node=nodes[i];i++)
        h.concat(results,Element.descendants(node));
      return results;
   },

    child:function(nodes){
      var h=Selector.handlers;
      for(var i=0,results=[],node;node=nodes[i];i++)
        h.concat(results,Element.immediateDescendants(node));
      return results;
   },

    adjacent:function(nodes){
      for(var i=0,results=[],node;node=nodes[i];i++){
        var next=this.nextElementSibling(node);
        if(next) results.push(next);
     }
      return results;
   },

    laterSibling:function(nodes){
      var h=Selector.handlers;
      for(var i=0,results=[],node;node=nodes[i];i++)
        h.concat(results,Element.nextSiblings(node));
      return results;
   },

    nextElementSibling:function(node){
      while(node=node.nextSibling)
	      if(node.nodeType==1) return node;
      return null;
   },

    previousElementSibling:function(node){
      while(node=node.previousSibling)
        if(node.nodeType==1) return node;
      return null;
   },

    // TOKEN FUNCTIONS
    tagName:function(nodes,root,tagName,combinator){
      tagName=tagName.toUpperCase();
      var results=[],h=Selector.handlers;
      if(nodes){
        if(combinator){
          // fastlane for ordinary descendant combinators
          if(combinator=="descendant"){
            for(var i=0,node;node=nodes[i];i++)
              h.concat(results,node.getElementsByTagName(tagName));
            return results;
         } else nodes=this[combinator](nodes);
          if(tagName=="*") return nodes;
       }
        for(var i=0,node;node=nodes[i];i++)
          if(node.tagName.toUpperCase()==tagName) results.push(node);
        return results;
     } else return root.getElementsByTagName(tagName);
   },

    id:function(nodes,root,id,combinator){
      var targetNode=$(id),h=Selector.handlers;
      if(!nodes && root==document) return targetNode?[targetNode] :[];
      if(nodes){
        if(combinator){
          if(combinator=='child'){
            for(var i=0,node;node=nodes[i];i++)
              if(targetNode.parentNode==node) return [targetNode];
         } else if(combinator=='descendant'){
            for(var i=0,node;node=nodes[i];i++)
              if(Element.descendantOf(targetNode,node)) return [targetNode];
         } else if(combinator=='adjacent'){
            for(var i=0,node;node=nodes[i];i++)
              if(Selector.handlers.previousElementSibling(targetNode)==node)
                return [targetNode];
         } else nodes=h[combinator](nodes);
       }
        for(var i=0,node;node=nodes[i];i++)
          if(node==targetNode) return [targetNode];
        return [];
     }
      return(targetNode && Element.descendantOf(targetNode,root))?[targetNode] :[];
   },

    className:function(nodes,root,className,combinator){
      if(nodes && combinator) nodes=this[combinator](nodes);
      return Selector.handlers.byClassName(nodes,root,className);
   },

    byClassName:function(nodes,root,className){
      if(!nodes) nodes=Selector.handlers.descendant([root]);
      var needle=' '+className+' ';
      for(var i=0,results=[],node,nodeClassName;node=nodes[i];i++){
        nodeClassName=node.className;
        if(nodeClassName.length==0) continue;
        if(nodeClassName==className ||(' '+nodeClassName+' ').include(needle))
          results.push(node);
     }
      return results;
   },

    attrPresence:function(nodes,root,attr){
      var results=[];
      for(var i=0,node;node=nodes[i];i++)
        if(Element.hasAttribute(node,attr)) results.push(node);
      return results;
   },

    attr:function(nodes,root,attr,value,operator){
      var handler=Selector.operators[operator],results=[];
      for(var i=0,node;node=nodes[i];i++){
        var nodeValue=Element.readAttribute(node,attr);
        if(nodeValue===null) continue;
        if(handler(nodeValue,value)) results.push(node);
     }
      return results;
   },

    pseudo:function(nodes,name,value,root,combinator){
      if(combinator) nodes=this[combinator](nodes);
      return Selector.pseudos[name](nodes,value,root);
   }
 },

  pseudos:{
    'first-child':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++){
        if(Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
     }
      return results;
   },
    'last-child':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++){
        if(Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
     }
      return results;
   },
    'only-child':function(nodes,value,root){
      var h=Selector.handlers;
      for(var i=0,results=[],node;node=nodes[i];i++)
        if(!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
   },
    'nth-child':      function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,formula,root);
   },
    'nth-last-child': function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,formula,root,true);
   },
    'nth-of-type':    function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,formula,root,false,true);
   },
    'nth-last-of-type':function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,formula,root,true,true);
   },
    'first-of-type':  function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,"1",root,false,true);
   },
    'last-of-type':   function(nodes,formula,root){
      return Selector.pseudos.nth(nodes,"1",root,true,true);
   },
    'only-of-type':   function(nodes,formula,root){
      var p=Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes,formula,root),formula,root);
   },

    // handles nth(-last)-child,nth(-last)-of-type,and(first|last)-of-type
    nth:function(nodes,formula,root,reverse,ofType){
      if(formula=='even') formula='2n+0';
      if(formula=='odd')  formula='2n+1';
      var h=Selector.handlers,results=[],indexed=[],m;
      h.mark(nodes);
      for(var i=0,node;node=nodes[i];i++){
        if(!node.parentNode._counted){
          h.index(node.parentNode,reverse,ofType);
          indexed.push(node.parentNode);
       }
     }
      if(formula.match(/^\d+$/)){// just a number
        formula=Number(formula);
        for(var i=0,node;node=nodes[i];i++)
          if(node.nodeIndex==formula) results.push(node);
     } else if(m=formula.match(/^(\d+)?n(\+(\d+))?$/)){// an+b
        var a=m[1]?Number(m[1]) :1;
        var b=m[3]?Number(m[3]) :0;
        for(var i=0,node;node=nodes[i];i++)
          if(node.nodeIndex % a==b) results.push(node);
     }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
   },

    'empty':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++){
        // IE treats comments as element nodes
        if(node.tagName=='!' ||(node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
        results.push(node);
     }
      return results;
   },

    'not':function(nodes,selector,root){
      var h=Selector.handlers,exclusions=$A(nodes),selectorType,m;
      for(var i in Selector.patterns){
        if(m=selector.match(Selector.patterns[i])){
          selectorType=i;break;
       }
     }
      switch(selectorType){
        case 'className':case 'tagName':case 'id':// fallthroughs
        case 'attrPresence':exclusions=h[selectorType](exclusions,root,m[1],false);break;
        case 'attr':m[3]=m[5] || m[6];exclusions=h.attr(exclusions,root,m[1],m[3],m[2]);break;
        case 'pseudo':exclusions=h.pseudo(exclusions,m[1],m[6],root,false);break;
        // only 'simple selectors'(one token) allowed in a :not clause
        default:throw 'Illegal selector in :not clause.';
     }
      h.mark(exclusions);
      for(var i=0,results=[],node;node=nodes[i];i++)
        if(!node._counted) results.push(node);
      h.unmark(exclusions);
      return results;
   },

    'enabled':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++)
        if(!node.disabled) results.push(node);
      return results;
   },

    'disabled':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++)
        if(node.disabled) results.push(node);
      return results;
   },

    'checked':function(nodes,value,root){
      for(var i=0,results=[],node;node=nodes[i];i++)
        if(node.checked) results.push(node);
      return results;
   }
 },

  operators:{
    '=':function(nv,v){return nv==v;},
    '!=':function(nv,v){return nv != v;},
    '^=':function(nv,v){return nv.startsWith(v);},
    '$=':function(nv,v){return nv.endsWith(v);},
    '*=':function(nv,v){return nv.include(v);},
    '~=':function(nv,v){return(' '+nv+' ').include(' '+v+' ');},
    '|=':function(nv,v){return('-'+nv.toUpperCase()+'-').include('-'+v.toUpperCase()+'-');}
 },

  matchElements:function(elements,expression){
    var matches=new Selector(expression).findElements(),h=Selector.handlers;
    h.mark(matches);
    for(var i=0,results=[],element;element=elements[i];i++)
      if(element._counted) results.push(element);
    h.unmark(matches);
    return results;
 },

  findElement:function(elements,expression,index){
    if(typeof expression=='number'){
      index=expression;expression=false;
   }
    return Selector.matchElements(elements,expression || '*')[index || 0];
 },

  findChildElements:function(element,expressions){
    var exprs=expressions.join(','),expressions=[];
    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(m){
      expressions.push(m[1].strip());
   });
    var results=[],h=Selector.handlers;
    for(var i=0,l=expressions.length,selector;i<l;i++){
      selector=new Selector(expressions[i].strip());
      h.concat(results,selector.findElements(element));
   }
    return(l>1)?h.unique(results) :results;
 }
});

function $$(){
  return Selector.findChildElements(document,$A(arguments));
}
var Form={
  reset:function(form){
    $(form).reset();
    return form;
 },

  serializeElements:function(elements,getHash){
    var data=elements.inject({},function(result,element){
      if(!element.disabled && element.name){
        var key=element.name,value=$(element).getValue();
        if(value != null){
         	if(key in result){
            if(result[key].constructor != Array) result[key]=[result[key]];
            result[key].push(value);
         }
          else result[key]=value;
       }
     }
      return result;
   });

    return getHash?data :Hash.toQueryString(data);
 }
};

Form.Methods={
  serialize:function(form,getHash){
    return Form.serializeElements(Form.getElements(form),getHash);
 },

  getElements:function(form){
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements,child){
        if(Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
     }
    );
 },

  getInputs:function(form,typeName,name){
    form=$(form);
    var inputs=form.getElementsByTagName('input');

    if(!typeName && !name) return $A(inputs).map(Element.extend);

    for(var i=0,matchingInputs=[],length=inputs.length;i<length;i++){
      var input=inputs[i];
      if((typeName && input.type != typeName) ||(name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
   }

    return matchingInputs;
 },

  disable:function(form){
    form=$(form);
    form.getElements().each(function(element){
      element.blur();
      element.disabled='true';
   });
    return form;
 },

  enable:function(form){
    form=$(form);
    form.getElements().each(function(element){
      element.disabled='';
   });
    return form;
 },

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

  focusFirstElement:function(form){
    form=$(form);
    form.findFirstElement().activate();
    return form;
 },

  request:function(form,options){
    form=$(form),options=Object.clone(options ||{});

    var params=options.parameters;
    options.parameters=form.serialize(true);

    if(params){
      if(typeof params=='string') params=params.toQueryParams();
      Object.extend(options.parameters,params);
   }

    if(form.hasAttribute('method') && !options.method)
      options.method=form.method;

    return new Ajax.Request(form.action,options);
 }
}

Object.extend(Form,Form.Methods);

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

Form.Element={
  focus:function(element){
    $(element).focus();
    return element;
 },

  select:function(element){
    $(element).select();
    return element;
 }
}

Form.Element.Methods={
  serialize:function(element){
    element=$(element);
    if(!element.disabled && element.name){
      var value=element.getValue();
      if(value != undefined){
        var pair={};
        pair[element.name]=value;
        return Hash.toQueryString(pair);
     }
   }
    return '';
 },

  getValue:function(element){
    element=$(element);
    var method=element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
 },

  clear:function(element){
    $(element).value='';
    return element;
 },

  present:function(element){
    return $(element).value != '';
 },

  activate:function(element){
    element=$(element);
    try{
      element.focus();
      if(element.select &&(element.tagName.toLowerCase() != 'input' ||
        !['button','reset','submit'].include(element.type)))
        element.select();
   } catch(e){}
    return element;
 },

  disable:function(element){
    element=$(element);
    element.blur();
    element.disabled=true;
    return element;
 },

  enable:function(element){
    element=$(element);
    element.disabled=false;
    return element;
 }
}

Object.extend(Form.Element,Form.Element.Methods);
Object.extend(Element.Methods.ByTag,{
  "FORM":   Object.clone(Form.Methods),
  "INPUT":  Object.clone(Form.Element.Methods),
  "SELECT": Object.clone(Form.Element.Methods),
  "TEXTAREA":Object.clone(Form.Element.Methods)
});

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

var Field=Form.Element;
var $F=Form.Element.getValue;

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

Form.Element.Serializers={
  input:function(element){
    switch(element.type.toLowerCase()){
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
      default:
        return Form.Element.Serializers.textarea(element);
   }
 },

  inputSelector:function(element){
    return element.checked?element.value :null;
 },

  textarea:function(element){
    return element.value;
 },

  select:function(element){
    return this[element.type=='select-one' ?
      'selectOne' :'selectMany'](element);
 },

  selectOne:function(element){
    var index=element.selectedIndex;
    return index >= 0?this.optionValue(element.options[index]) :null;
 },

  selectMany:function(element){
    var values,length=element.length;
    if(!length) return null;

    for(var i=0,values=[];i<length;i++){
      var opt=element.options[i];
      if(opt.selected) values.push(this.optionValue(opt));
   }
    return values;
 },

  optionValue:function(opt){
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value')?opt.value :opt.text;
 }
}

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

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();
    var changed=('string'==typeof this.lastValue && 'string'==typeof value
     ?this.lastValue != value :String(this.lastValue) != String(value));
    if(changed){
      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(){
    Form.getElements(this.element).each(this.registerCallback.bind(this));
 },

  registerCallback:function(element){
    if(element.type){
      switch(element.type.toLowerCase()){
        case 'checkbox':
        case 'radio':
          Event.observe(element,'click',this.onElementEvent.bind(this));
          break;
        default:
          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,
  KEY_HOME:   36,
  KEY_END:    35,
  KEY_PAGEUP: 33,
  KEY_PAGEDOWN:34,

  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,length=Event.observers.length;i<length;i++){
      Event.stopObserving.apply(this,Event.observers[i]);
      Event.observers[i][0]=null;
   }
    Event.observers=false;
 },

  observe:function(element,name,observer,useCapture){
    element=$(element);
    useCapture=useCapture || false;

    if(name=='keypress' &&
     (Prototype.Browser.WebKit || element.attachEvent))
      name='keydown';

    Event._observeAndCache(element,name,observer,useCapture);
 },

  stopObserving:function(element,name,observer,useCapture){
    element=$(element);
    useCapture=useCapture || false;

    if(name=='keypress' &&
       (Prototype.Browser.WebKit || element.attachEvent))
      name='keydown';

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

/* prevent memory leaks in IE */
if(Prototype.Browser.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){
        if(element.tagName=='BODY') break;
        var 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;
 },

  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{
      if(!window.opera || element.tagName=='BODY'){
        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(Prototype.Browser.WebKit){
  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];
 }
}

Element.addMethods();