From 0f00e85e311955b3f84dde559da6b5a2ab5c3cda Mon Sep 17 00:00:00 2001
From: xue <>
Date: Mon, 26 Mar 2007 00:27:59 +0000
Subject: merge from 3.0 branch till 1769.

---
 framework/Web/Javascripts/js/compressed/effects.js |   82 +-
 framework/Web/Javascripts/js/compressed/prado.js   |  447 ++-
 framework/Web/Javascripts/js/debug/effects.js      |  457 ++-
 framework/Web/Javascripts/js/debug/prado.js        | 4143 ++++++++++++++------
 4 files changed, 3519 insertions(+), 1610 deletions(-)

(limited to 'framework/Web/Javascripts/js')

diff --git a/framework/Web/Javascripts/js/compressed/effects.js b/framework/Web/Javascripts/js/compressed/effects.js
index 1a2915e6..50b64f7a 100644
--- a/framework/Web/Javascripts/js/compressed/effects.js
+++ b/framework/Web/Javascripts/js/compressed/effects.js
@@ -3,65 +3,59 @@ String.prototype.parseColor=function(){var color='#';if(this.slice(0,4)=='rgb(')
 return(color.length==7?color:(arguments[0]||this));}
 Element.collectTextNodes=function(element){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:(node.hasChildNodes()?Element.collectTextNodes(node):''));}).flatten().join('');}
 Element.collectTextNodesIgnoreClass=function(element,className){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:((node.hasChildNodes()&&!Element.hasClassName(node,className))?Element.collectTextNodesIgnoreClass(node,className):''));}).flatten().join('');}
-Element.setContentZoom=function(element,percent){element=$(element);Element.setStyle(element,{fontSize:(percent/100)+'em'});if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);}
-Element.getOpacity=function(element){var opacity;if(opacity=Element.getStyle(element,'opacity'))
-return parseFloat(opacity);if(opacity=(Element.getStyle(element,'filter')||'').match(/alpha\(opacity=(.*)\)/))
-if(opacity[1])return parseFloat(opacity[1])/100;return 1.0;}
-Element.setOpacity=function(element,value){element=$(element);if(value==1){Element.setStyle(element,{opacity:(/Gecko/.test(navigator.userAgent)&&!/Konqueror|Safari|KHTML/.test(navigator.userAgent))?0.999999:null});if(/MSIE/.test(navigator.userAgent))
-Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});}else{if(value<0.00001)value=0;Element.setStyle(element,{opacity:value});if(/MSIE/.test(navigator.userAgent))
-Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')+'alpha(opacity='+value*100+')'});}}
+Element.setContentZoom=function(element,percent){element=$(element);element.setStyle({fontSize:(percent/100)+'em'});if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);return element;}
+Element.getOpacity=function(element){return $(element).getStyle('opacity');}
+Element.setOpacity=function(element,value){return $(element).setStyle({opacity:value});}
 Element.getInlineOpacity=function(element){return $(element).style.opacity||'';}
-Element.childrenWithClassName=function(element,className,findFirst){var classNameRegExp=new RegExp("(^|\\s)"+className+"(\\s|$)");var results=$A($(element).getElementsByTagName('*'))[findFirst?'detect':'select'](function(c){return(c.className&&c.className.match(classNameRegExp));});if(!results)results=[];return results;}
 Element.forceRerendering=function(element){try{element=$(element);var n=document.createTextNode(' ');element.appendChild(n);element.removeChild(n);}catch(e){}};Array.prototype.call=function(){var args=arguments;this.each(function(f){f.apply(this,args)});}
-var Effect={tagifyText:function(element){if(typeof Builder=='undefined')
-throw("Effect.tagifyText requires including script.aculo.us' builder.js library");var tagifyStyle='position:relative';if(/MSIE/.test(navigator.userAgent))tagifyStyle+=';zoom:1';element=$(element);$A(element.childNodes).each(function(child){if(child.nodeType==3){child.nodeValue.toArray().each(function(character){element.insertBefore(Builder.node('span',{style:tagifyStyle},character==' '?String.fromCharCode(160):character),child);});Element.remove(child);}});},multiple:function(element,effect){var elements;if(((typeof element=='object')||(typeof element=='function'))&&(element.length))
+var Effect={_elementDoesNotExistError:{name:'ElementDoesNotExistError',message:'The specified DOM element does not exist, but is required for this effect to operate'},tagifyText:function(element){if(typeof Builder=='undefined')
+throw("Effect.tagifyText requires including script.aculo.us' builder.js library");var tagifyStyle='position:relative';if(/MSIE/.test(navigator.userAgent)&&!window.opera)tagifyStyle+=';zoom:1';element=$(element);$A(element.childNodes).each(function(child){if(child.nodeType==3){child.nodeValue.toArray().each(function(character){element.insertBefore(Builder.node('span',{style:tagifyStyle},character==' '?String.fromCharCode(160):character),child);});Element.remove(child);}});},multiple:function(element,effect){var elements;if(((typeof element=='object')||(typeof element=='function'))&&(element.length))
 elements=element;else
-elements=$(element).childNodes;var options=Object.extend({speed:0.1,delay:0.0},arguments[2]||{});var masterDelay=options.delay;$A(elements).each(function(element,index){new effect(element,Object.extend(options,{delay:index*options.speed+masterDelay}));});},PAIRS:{'slide':['SlideDown','SlideUp'],'blind':['BlindDown','BlindUp'],'appear':['Appear','Fade']},toggle:function(element,effect){element=$(element);effect=(effect||'appear').toLowerCase();var options=Object.extend({queue:{position:'end',scope:(element.id||'global'),limit:1}},arguments[2]||{});Effect[element.visible()?Effect.PAIRS[effect][1]:Effect.PAIRS[effect][0]](element,options);}};var Effect2=Effect;Effect.Transitions={}
-Effect.Transitions.linear=Prototype.K;Effect.Transitions.sinoidal=function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;}
-Effect.Transitions.reverse=function(pos){return 1-pos;}
-Effect.Transitions.flicker=function(pos){return((-Math.cos(pos*Math.PI)/4)+0.75)+Math.random()/4;}
-Effect.Transitions.wobble=function(pos){return(-Math.cos(pos*Math.PI*(9*pos))/2)+0.5;}
-Effect.Transitions.pulse=function(pos){return(Math.floor(pos*10)%2==0?(pos*10-Math.floor(pos*10)):1-(pos*10-Math.floor(pos*10)));}
-Effect.Transitions.none=function(pos){return 0;}
-Effect.Transitions.full=function(pos){return 1;}
-Effect.ScopedQueue=Class.create();Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null;},_each:function(iterator){this.effects._each(iterator);},add:function(effect){var timestamp=new Date().getTime();var position=(typeof effect.options.queue=='string')?effect.options.queue:effect.options.queue.position;switch(position){case'front':this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){e.startOn+=effect.finishOn;e.finishOn+=effect.finishOn;});break;case'end':timestamp=this.effects.pluck('finishOn').max()||timestamp;break;}
+elements=$(element).childNodes;var options=Object.extend({speed:0.1,delay:0.0},arguments[2]||{});var masterDelay=options.delay;$A(elements).each(function(element,index){new effect(element,Object.extend(options,{delay:index*options.speed+masterDelay}));});},PAIRS:{'slide':['SlideDown','SlideUp'],'blind':['BlindDown','BlindUp'],'appear':['Appear','Fade']},toggle:function(element,effect){element=$(element);effect=(effect||'appear').toLowerCase();var options=Object.extend({queue:{position:'end',scope:(element.id||'global'),limit:1}},arguments[2]||{});Effect[element.visible()?Effect.PAIRS[effect][1]:Effect.PAIRS[effect][0]](element,options);}};var Effect2=Effect;Effect.Transitions={linear:Prototype.K,sinoidal:function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;},reverse:function(pos){return 1-pos;},flicker:function(pos){return((-Math.cos(pos*Math.PI)/4)+0.75)+Math.random()/4;},wobble:function(pos){return(-Math.cos(pos*Math.PI*(9*pos))/2)+0.5;},pulse:function(pos,pulses){pulses=pulses||5;return(Math.round((pos%(1/pulses))*pulses)==0?((pos*pulses*2)-Math.floor(pos*pulses*2)):1-((pos*pulses*2)-Math.floor(pos*pulses*2)));},none:function(pos){return 0;},full:function(pos){return 1;}};Effect.ScopedQueue=Class.create();Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null;},_each:function(iterator){this.effects._each(iterator);},add:function(effect){var timestamp=new Date().getTime();var position=(typeof effect.options.queue=='string')?effect.options.queue:effect.options.queue.position;switch(position){case'front':this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){e.startOn+=effect.finishOn;e.finishOn+=effect.finishOn;});break;case'with-last':timestamp=this.effects.pluck('startOn').max()||timestamp;break;case'end':timestamp=this.effects.pluck('finishOn').max()||timestamp;break;}
 effect.startOn+=timestamp;effect.finishOn+=timestamp;if(!effect.options.queue.limit||(this.effects.length<effect.options.queue.limit))
 this.effects.push(effect);if(!this.interval)
-this.interval=setInterval(this.loop.bind(this),40);},remove:function(effect){this.effects=this.effects.reject(function(e){return e==effect});if(this.effects.length==0){clearInterval(this.interval);this.interval=null;}},loop:function(){var timePos=new Date().getTime();this.effects.invoke('loop',timePos);}});Effect.Queues={instances:$H(),get:function(queueName){if(typeof queueName!='string')return queueName;if(!this.instances[queueName])
+this.interval=setInterval(this.loop.bind(this),15);},remove:function(effect){this.effects=this.effects.reject(function(e){return e==effect});if(this.effects.length==0){clearInterval(this.interval);this.interval=null;}},loop:function(){var timePos=new Date().getTime();for(var i=0,len=this.effects.length;i<len;i++)
+if(this.effects[i])this.effects[i].loop(timePos);}});Effect.Queues={instances:$H(),get:function(queueName){if(typeof queueName!='string')return queueName;if(!this.instances[queueName])
 this.instances[queueName]=new Effect.ScopedQueue();return this.instances[queueName];}}
-Effect.Queue=Effect.Queues.get('global');Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1.0,fps:25.0,sync:false,from:0.0,to:1.0,delay:0.0,queue:'parallel'}
+Effect.Queue=Effect.Queues.get('global');Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1.0,fps:60.0,sync:false,from:0.0,to:1.0,delay:0.0,queue:'parallel'}
 Effect.Base=function(){};Effect.Base.prototype={position:null,start:function(options){this.options=Object.extend(Object.extend({},Effect.DefaultOptions),options||{});this.currentFrame=0;this.state='idle';this.startOn=this.options.delay*1000;this.finishOn=this.startOn+(this.options.duration*1000);this.event('beforeStart');if(!this.options.sync)
 Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).add(this);},loop:function(timePos){if(timePos>=this.startOn){if(timePos>=this.finishOn){this.render(1.0);this.cancel();this.event('beforeFinish');if(this.finish)this.finish();this.event('afterFinish');return;}
 var pos=(timePos-this.startOn)/(this.finishOn-this.startOn);var frame=Math.round(pos*this.options.fps*this.options.duration);if(frame>this.currentFrame){this.render(pos);this.currentFrame=frame;}}},render:function(pos){if(this.state=='idle'){this.state='running';this.event('beforeSetup');if(this.setup)this.setup();this.event('afterSetup');}
 if(this.state=='running'){if(this.options.transition)pos=this.options.transition(pos);pos*=(this.options.to-this.options.from);pos+=this.options.from;this.position=pos;this.event('beforeUpdate');if(this.update)this.update(pos);this.event('afterUpdate');}},cancel:function(){if(!this.options.sync)
-Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).remove(this);this.state='finished';},event:function(eventName){if(this.options[eventName+'Internal'])this.options[eventName+'Internal'](this);if(this.options[eventName])this.options[eventName](this);},inspect:function(){return'#<Effect:'+$H(this).inspect()+',options:'+$H(this.options).inspect()+'>';}}
-Effect.Parallel=Class.create();Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(effects){this.effects=effects||[];this.start(arguments[1]);},update:function(position){this.effects.invoke('render',position);},finish:function(position){this.effects.each(function(effect){effect.render(1.0);effect.cancel();effect.event('beforeFinish');if(effect.finish)effect.finish(position);effect.event('afterFinish');});}});Effect.Opacity=Class.create();Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(/MSIE/.test(navigator.userAgent)&&(!this.element.currentStyle.hasLayout))
-this.element.setStyle({zoom:1});var options=Object.extend({from:this.element.getOpacity()||0.0,to:1.0},arguments[1]||{});this.start(options);},update:function(position){this.element.setOpacity(position);}});Effect.Move=Class.create();Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({x:0,y:0,mode:'relative'},arguments[1]||{});this.start(options);},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle('left')||'0');this.originalTop=parseFloat(this.element.getStyle('top')||'0');if(this.options.mode=='absolute'){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop;}},update:function(position){this.element.setStyle({left:Math.round(this.options.x*position+this.originalLeft)+'px',top:Math.round(this.options.y*position+this.originalTop)+'px'});}});Effect.MoveBy=function(element,toTop,toLeft){return new Effect.Move(element,Object.extend({x:toLeft,y:toTop},arguments[3]||{}));};Effect.Scale=Class.create();Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(element,percent){this.element=$(element)
-var options=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:'box',scaleFrom:100.0,scaleTo:percent},arguments[2]||{});this.start(options);},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle('position');this.originalStyle={};['top','left','width','height','fontSize'].each(function(k){this.originalStyle[k]=this.element.style[k];}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var fontSize=this.element.getStyle('font-size')||'100%';['em','px','%','pt'].each(function(fontSizeType){if(fontSize.indexOf(fontSizeType)>0){this.fontSize=parseFloat(fontSize);this.fontSizeType=fontSizeType;}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=='box')
+Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).remove(this);this.state='finished';},event:function(eventName){if(this.options[eventName+'Internal'])this.options[eventName+'Internal'](this);if(this.options[eventName])this.options[eventName](this);},inspect:function(){var data=$H();for(property in this)
+if(typeof this[property]!='function')data[property]=this[property];return'#<Effect:'+data.inspect()+',options:'+$H(this.options).inspect()+'>';}}
+Effect.Parallel=Class.create();Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(effects){this.effects=effects||[];this.start(arguments[1]);},update:function(position){this.effects.invoke('render',position);},finish:function(position){this.effects.each(function(effect){effect.render(1.0);effect.cancel();effect.event('beforeFinish');if(effect.finish)effect.finish(position);effect.event('afterFinish');});}});Effect.Event=Class.create();Object.extend(Object.extend(Effect.Event.prototype,Effect.Base.prototype),{initialize:function(){var options=Object.extend({duration:0},arguments[0]||{});this.start(options);},update:Prototype.emptyFunction});Effect.Opacity=Class.create();Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);if(/MSIE/.test(navigator.userAgent)&&!window.opera&&(!this.element.currentStyle.hasLayout))
+this.element.setStyle({zoom:1});var options=Object.extend({from:this.element.getOpacity()||0.0,to:1.0},arguments[1]||{});this.start(options);},update:function(position){this.element.setOpacity(position);}});Effect.Move=Class.create();Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({x:0,y:0,mode:'relative'},arguments[1]||{});this.start(options);},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle('left')||'0');this.originalTop=parseFloat(this.element.getStyle('top')||'0');if(this.options.mode=='absolute'){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop;}},update:function(position){this.element.setStyle({left:Math.round(this.options.x*position+this.originalLeft)+'px',top:Math.round(this.options.y*position+this.originalTop)+'px'});}});Effect.MoveBy=function(element,toTop,toLeft){return new Effect.Move(element,Object.extend({x:toLeft,y:toTop},arguments[3]||{}));};Effect.Scale=Class.create();Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(element,percent){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:'box',scaleFrom:100.0,scaleTo:percent},arguments[2]||{});this.start(options);},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle('position');this.originalStyle={};['top','left','width','height','fontSize'].each(function(k){this.originalStyle[k]=this.element.style[k];}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var fontSize=this.element.getStyle('font-size')||'100%';['em','px','%','pt'].each(function(fontSizeType){if(fontSize.indexOf(fontSizeType)>0){this.fontSize=parseFloat(fontSize);this.fontSizeType=fontSizeType;}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=='box')
 this.dims=[this.element.offsetHeight,this.element.offsetWidth];if(/^content/.test(this.options.scaleMode))
 this.dims=[this.element.scrollHeight,this.element.scrollWidth];if(!this.dims)
 this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth];},update:function(position){var currentScale=(this.options.scaleFrom/100.0)+(this.factor*position);if(this.options.scaleContent&&this.fontSize)
 this.element.setStyle({fontSize:this.fontSize*currentScale+this.fontSizeType});this.setDimensions(this.dims[0]*currentScale,this.dims[1]*currentScale);},finish:function(position){if(this.restoreAfterFinish)this.element.setStyle(this.originalStyle);},setDimensions:function(height,width){var d={};if(this.options.scaleX)d.width=Math.round(width)+'px';if(this.options.scaleY)d.height=Math.round(height)+'px';if(this.options.scaleFromCenter){var topd=(height-this.dims[0])/2;var leftd=(width-this.dims[1])/2;if(this.elementPositioning=='absolute'){if(this.options.scaleY)d.top=this.originalTop-topd+'px';if(this.options.scaleX)d.left=this.originalLeft-leftd+'px';}else{if(this.options.scaleY)d.top=-topd+'px';if(this.options.scaleX)d.left=-leftd+'px';}}
-this.element.setStyle(d);}});Effect.Highlight=Class.create();Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({startcolor:'#ffff99'},arguments[1]||{});this.start(options);},setup:function(){if(this.element.getStyle('display')=='none'){this.cancel();return;}
-this.oldStyle={backgroundImage:this.element.getStyle('background-image')};this.element.setStyle({backgroundImage:'none'});if(!this.options.endcolor)
+this.element.setStyle(d);}});Effect.Highlight=Class.create();Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({startcolor:'#ffff99'},arguments[1]||{});this.start(options);},setup:function(){if(this.element.getStyle('display')=='none'){this.cancel();return;}
+this.oldStyle={};if(!this.options.keepBackgroundImage){this.oldStyle.backgroundImage=this.element.getStyle('background-image');this.element.setStyle({backgroundImage:'none'});}
+if(!this.options.endcolor)
 this.options.endcolor=this.element.getStyle('background-color').parseColor('#ffffff');if(!this.options.restorecolor)
 this.options.restorecolor=this.element.getStyle('background-color');this._base=$R(0,2).map(function(i){return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16)}.bind(this));this._delta=$R(0,2).map(function(i){return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]}.bind(this));},update:function(position){this.element.setStyle({backgroundColor:$R(0,2).inject('#',function(m,v,i){return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart());}.bind(this))});},finish:function(){this.element.setStyle(Object.extend(this.oldStyle,{backgroundColor:this.options.restorecolor}));}});Effect.ScrollTo=Class.create();Object.extend(Object.extend(Effect.ScrollTo.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);this.start(arguments[1]||{});},setup:function(){Position.prepare();var offsets=Position.cumulativeOffset(this.element);if(this.options.offset)offsets[1]+=this.options.offset;var max=window.innerHeight?window.height-window.innerHeight:document.body.scrollHeight-
-(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=(offsets[1]>max?max:offsets[1])-this.scrollStart;},update:function(position){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+(position*this.delta));}});Effect.Fade=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();var options=Object.extend({from:element.getOpacity()||1.0,to:0.0,afterFinishInternal:function(effect){if(effect.options.to!=0)return;effect.element.hide();effect.element.setStyle({opacity:oldOpacity});}},arguments[1]||{});return new Effect.Opacity(element,options);}
-Effect.Appear=function(element){element=$(element);var options=Object.extend({from:(element.getStyle('display')=='none'?0.0:element.getOpacity()||0.0),to:1.0,afterFinishInternal:function(effect){effect.element.forceRerendering();},beforeSetup:function(effect){effect.element.setOpacity(effect.options.from);effect.element.show();}},arguments[1]||{});return new Effect.Opacity(element,options);}
-Effect.Puff=function(element){element=$(element);var oldStyle={opacity:element.getInlineOpacity(),position:element.getStyle('position')};return new Effect.Parallel([new Effect.Scale(element,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:1.0,beforeSetupInternal:function(effect){effect.effects[0].element.setStyle({position:'absolute'});},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.setStyle(oldStyle);}},arguments[1]||{}));}
-Effect.BlindUp=function(element){element=$(element);element.makeClipping();return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();}},arguments[1]||{}));}
-Effect.BlindDown=function(element){element=$(element);var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makeClipping();effect.element.setStyle({height:'0px'});effect.element.show();},afterFinishInternal:function(effect){effect.element.undoClipping();}},arguments[1]||{}));}
-Effect.SwitchOff=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();return new Effect.Appear(element,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(effect){new Effect.Scale(effect.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makePositioned();effect.element.makeClipping();},afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.undoPositioned();effect.element.setStyle({opacity:oldOpacity});}})}},arguments[1]||{}));}
-Effect.DropOut=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left'),opacity:element.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(element,{x:0,y:100,sync:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:0.5,beforeSetup:function(effect){effect.effects[0].element.makePositioned();},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},arguments[1]||{}));}
-Effect.Shake=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left')};return new Effect.Move(element,{x:20,y:0,duration:0.05,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-20,y:0,duration:0.05,afterFinishInternal:function(effect){effect.element.undoPositioned();effect.element.setStyle(oldStyle);}})}})}})}})}})}});}
-Effect.SlideDown=function(element){element=$(element);element.cleanWhitespace();var oldInnerBottom=$(element.firstChild).getStyle('bottom');var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makePositioned();effect.element.firstChild.makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping();effect.element.setStyle({height:'0px'});effect.element.show();},afterUpdateInternal:function(effect){effect.element.firstChild.setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.undoClipping();if(/MSIE/.test(navigator.userAgent)){effect.element.undoPositioned();effect.element.firstChild.undoPositioned();}else{effect.element.firstChild.undoPositioned();effect.element.undoPositioned();}
-effect.element.firstChild.setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
-Effect.SlideUp=function(element){element=$(element);element.cleanWhitespace();var oldInnerBottom=$(element.firstChild).getStyle('bottom');return new Effect.Scale(element,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:'box',scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(effect){effect.element.makePositioned();effect.element.firstChild.makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping();effect.element.show();},afterUpdateInternal:function(effect){effect.element.firstChild.setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.firstChild.undoPositioned();effect.element.undoPositioned();effect.element.setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
-Effect.Squish=function(element){return new Effect.Scale(element,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makeClipping(effect.element);},afterFinishInternal:function(effect){effect.element.hide(effect.element);effect.element.undoClipping(effect.element);}});}
+(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=(offsets[1]>max?max:offsets[1])-this.scrollStart;},update:function(position){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+(position*this.delta));}});Effect.Fade=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();var options=Object.extend({from:element.getOpacity()||1.0,to:0.0,afterFinishInternal:function(effect){if(effect.options.to!=0)return;effect.element.hide().setStyle({opacity:oldOpacity});}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Appear=function(element){element=$(element);var options=Object.extend({from:(element.getStyle('display')=='none'?0.0:element.getOpacity()||0.0),to:1.0,afterFinishInternal:function(effect){effect.element.forceRerendering();},beforeSetup:function(effect){effect.element.setOpacity(effect.options.from).show();}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Puff=function(element){element=$(element);var oldStyle={opacity:element.getInlineOpacity(),position:element.getStyle('position'),top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};return new Effect.Parallel([new Effect.Scale(element,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:1.0,beforeSetupInternal:function(effect){Position.absolutize(effect.effects[0].element)},afterFinishInternal:function(effect){effect.effects[0].element.hide().setStyle(oldStyle);}},arguments[1]||{}));}
+Effect.BlindUp=function(element){element=$(element);element.makeClipping();return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(effect){effect.element.hide().undoClipping();}},arguments[1]||{}));}
+Effect.BlindDown=function(element){element=$(element);var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makeClipping().setStyle({height:'0px'}).show();},afterFinishInternal:function(effect){effect.element.undoClipping();}},arguments[1]||{}));}
+Effect.SwitchOff=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();return new Effect.Appear(element,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(effect){new Effect.Scale(effect.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makePositioned().makeClipping();},afterFinishInternal:function(effect){effect.element.hide().undoClipping().undoPositioned().setStyle({opacity:oldOpacity});}})}},arguments[1]||{}));}
+Effect.DropOut=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left'),opacity:element.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(element,{x:0,y:100,sync:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:0.5,beforeSetup:function(effect){effect.effects[0].element.makePositioned();},afterFinishInternal:function(effect){effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);}},arguments[1]||{}));}
+Effect.Shake=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left')};return new Effect.Move(element,{x:20,y:0,duration:0.05,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-20,y:0,duration:0.05,afterFinishInternal:function(effect){effect.element.undoPositioned().setStyle(oldStyle);}})}})}})}})}})}});}
+Effect.SlideDown=function(element){element=$(element).cleanWhitespace();var oldInnerBottom=element.down().getStyle('bottom');var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makePositioned();effect.element.down().makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping().setStyle({height:'0px'}).show();},afterUpdateInternal:function(effect){effect.element.down().setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.undoClipping().undoPositioned();effect.element.down().undoPositioned().setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
+Effect.SlideUp=function(element){element=$(element).cleanWhitespace();var oldInnerBottom=element.down().getStyle('bottom');return new Effect.Scale(element,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:'box',scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(effect){effect.element.makePositioned();effect.element.down().makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping().show();},afterUpdateInternal:function(effect){effect.element.down().setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.hide().undoClipping().undoPositioned().setStyle({bottom:oldInnerBottom});effect.element.down().undoPositioned();}},arguments[1]||{}));}
+Effect.Squish=function(element){return new Effect.Scale(element,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makeClipping();},afterFinishInternal:function(effect){effect.element.hide().undoClipping();}});}
 Effect.Grow=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.full},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:element.getInlineOpacity()};var dims=element.getDimensions();var initialMoveX,initialMoveY;var moveX,moveY;switch(options.direction){case'top-left':initialMoveX=initialMoveY=moveX=moveY=0;break;case'top-right':initialMoveX=dims.width;initialMoveY=moveY=0;moveX=-dims.width;break;case'bottom-left':initialMoveX=moveX=0;initialMoveY=dims.height;moveY=-dims.height;break;case'bottom-right':initialMoveX=dims.width;initialMoveY=dims.height;moveX=-dims.width;moveY=-dims.height;break;case'center':initialMoveX=dims.width/2;initialMoveY=dims.height/2;moveX=-dims.width/2;moveY=-dims.height/2;break;}
-return new Effect.Move(element,{x:initialMoveX,y:initialMoveY,duration:0.01,beforeSetup:function(effect){effect.element.hide();effect.element.makeClipping();effect.element.makePositioned();},afterFinishInternal:function(effect){new Effect.Parallel([new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),new Effect.Scale(effect.element,100,{scaleMode:{originalHeight:dims.height,originalWidth:dims.width},sync:true,scaleFrom:window.opera?1:0,transition:options.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(effect){effect.effects[0].element.setStyle({height:'0px'});effect.effects[0].element.show();},afterFinishInternal:function(effect){effect.effects[0].element.undoClipping();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},options))}});}
+return new Effect.Move(element,{x:initialMoveX,y:initialMoveY,duration:0.01,beforeSetup:function(effect){effect.element.hide().makeClipping().makePositioned();},afterFinishInternal:function(effect){new Effect.Parallel([new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),new Effect.Scale(effect.element,100,{scaleMode:{originalHeight:dims.height,originalWidth:dims.width},sync:true,scaleFrom:window.opera?1:0,transition:options.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(effect){effect.effects[0].element.setStyle({height:'0px'}).show();},afterFinishInternal:function(effect){effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);}},options))}});}
 Effect.Shrink=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.none},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:element.getInlineOpacity()};var dims=element.getDimensions();var moveX,moveY;switch(options.direction){case'top-left':moveX=moveY=0;break;case'top-right':moveX=dims.width;moveY=0;break;case'bottom-left':moveX=0;moveY=dims.height;break;case'bottom-right':moveX=dims.width;moveY=dims.height;break;case'center':moveX=dims.width/2;moveY=dims.height/2;break;}
-return new Effect.Parallel([new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),new Effect.Scale(element,window.opera?1:0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})],Object.extend({beforeStartInternal:function(effect){effect.effects[0].element.makePositioned();effect.effects[0].element.makeClipping();},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.undoClipping();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},options));}
-Effect.Pulsate=function(element){element=$(element);var options=arguments[1]||{};var oldOpacity=element.getInlineOpacity();var transition=options.transition||Effect.Transitions.sinoidal;var reverser=function(pos){return transition(1-Effect.Transitions.pulse(pos))};reverser.bind(transition);return new Effect.Opacity(element,Object.extend(Object.extend({duration:3.0,from:0,afterFinishInternal:function(effect){effect.element.setStyle({opacity:oldOpacity});}},options),{transition:reverser}));}
-Effect.Fold=function(element){element=$(element);var oldStyle={top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};Element.makeClipping(element);return new Effect.Scale(element,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(effect){new Effect.Scale(element,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.setStyle(oldStyle);}});}},arguments[1]||{}));};['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom','collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(function(f){Element.Methods[f]=Element[f];});Element.Methods.visualEffect=function(element,effect,options){s=effect.gsub(/_/,'-').camelize();effect_class=s.charAt(0).toUpperCase()+s.substring(1);new Effect[effect_class](element,options);return $(element);};Element.addMethods();Prado.Effect={Highlight:function(element,options)
-{new Effect.Highlight(element,options||{});}}
\ No newline at end of file
+return new Effect.Parallel([new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),new Effect.Scale(element,window.opera?1:0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})],Object.extend({beforeStartInternal:function(effect){effect.effects[0].element.makePositioned().makeClipping();},afterFinishInternal:function(effect){effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle);}},options));}
+Effect.Pulsate=function(element){element=$(element);var options=arguments[1]||{};var oldOpacity=element.getInlineOpacity();var transition=options.transition||Effect.Transitions.sinoidal;var reverser=function(pos){return transition(1-Effect.Transitions.pulse(pos,options.pulses))};reverser.bind(transition);return new Effect.Opacity(element,Object.extend(Object.extend({duration:2.0,from:0,afterFinishInternal:function(effect){effect.element.setStyle({opacity:oldOpacity});}},options),{transition:reverser}));}
+Effect.Fold=function(element){element=$(element);var oldStyle={top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};element.makeClipping();return new Effect.Scale(element,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(effect){new Effect.Scale(element,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(effect){effect.element.hide().undoClipping().setStyle(oldStyle);}});}},arguments[1]||{}));};Effect.Morph=Class.create();Object.extend(Object.extend(Effect.Morph.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({style:{}},arguments[1]||{});if(typeof options.style=='string'){if(options.style.indexOf(':')==-1){var cssText='',selector='.'+options.style;$A(document.styleSheets).reverse().each(function(styleSheet){if(styleSheet.cssRules)cssRules=styleSheet.cssRules;else if(styleSheet.rules)cssRules=styleSheet.rules;$A(cssRules).reverse().each(function(rule){if(selector==rule.selectorText){cssText=rule.style.cssText;throw $break;}});if(cssText)throw $break;});this.style=cssText.parseStyle();options.afterFinishInternal=function(effect){effect.element.addClassName(effect.options.style);effect.transforms.each(function(transform){if(transform.style!='opacity')
+effect.element.style[transform.style.camelize()]='';});}}else this.style=options.style.parseStyle();}else this.style=$H(options.style)
+this.start(options);},setup:function(){function parseColor(color){if(!color||['rgba(0, 0, 0, 0)','transparent'].include(color))color='#ffffff';color=color.parseColor();return $R(0,2).map(function(i){return parseInt(color.slice(i*2+1,i*2+3),16)});}
+this.transforms=this.style.map(function(pair){var property=pair[0].underscore().dasherize(),value=pair[1],unit=null;if(value.parseColor('#zzzzzz')!='#zzzzzz'){value=value.parseColor();unit='color';}else if(property=='opacity'){value=parseFloat(value);if(/MSIE/.test(navigator.userAgent)&&!window.opera&&(!this.element.currentStyle.hasLayout))
+this.element.setStyle({zoom:1});}else if(Element.CSS_LENGTH.test(value))
+var components=value.match(/^([\+\-]?[0-9\.]+)(.*)$/),value=parseFloat(components[1]),unit=(components.length==3)?components[2]:null;var originalValue=this.element.getStyle(property);return $H({style:property,originalValue:unit=='color'?parseColor(originalValue):parseFloat(originalValue||0),targetValue:unit=='color'?parseColor(value):value,unit:unit});}.bind(this)).reject(function(transform){return((transform.originalValue==transform.targetValue)||(transform.unit!='color'&&(isNaN(transform.originalValue)||isNaN(transform.targetValue))))});},update:function(position){var style=$H(),value=null;this.transforms.each(function(transform){value=transform.unit=='color'?$R(0,2).inject('#',function(m,v,i){return m+(Math.round(transform.originalValue[i]+
+(transform.targetValue[i]-transform.originalValue[i])*position)).toColorPart()}):transform.originalValue+Math.round(((transform.targetValue-transform.originalValue)*position)*1000)/1000+transform.unit;style[transform.style]=value;});this.element.setStyle(style);}});Effect.Transform=Class.create();Object.extend(Effect.Transform.prototype,{initialize:function(tracks){this.tracks=[];this.options=arguments[1]||{};this.addTracks(tracks);},addTracks:function(tracks){tracks.each(function(track){var data=$H(track).values().first();this.tracks.push($H({ids:$H(track).keys().first(),effect:Effect.Morph,options:{style:data}}));}.bind(this));return this;},play:function(){return new Effect.Parallel(this.tracks.map(function(track){var elements=[$(track.ids)||$$(track.ids)].flatten();return elements.map(function(e){return new track.effect(e,Object.extend({sync:true},track.options))});}).flatten(),this.options);}});Element.CSS_PROPERTIES=$w('backgroundColor backgroundPosition borderBottomColor borderBottomStyle '+'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth '+'borderRightColor borderRightStyle borderRightWidth borderSpacing '+'borderTopColor borderTopStyle borderTopWidth bottom clip color '+'fontSize fontWeight height left letterSpacing lineHeight '+'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+'maxWidth minHeight minWidth opacity outlineColor outlineOffset '+'outlineWidth paddingBottom paddingLeft paddingRight paddingTop '+'right textIndent top width wordSpacing zIndex');Element.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;String.prototype.parseStyle=function(){var element=Element.extend(document.createElement('div'));element.innerHTML='<div style="'+this+'"></div>';var style=element.down().style,styleRules=$H();Element.CSS_PROPERTIES.each(function(property){if(style[property])styleRules[property]=style[property];});if(/MSIE/.test(navigator.userAgent)&&!window.opera&&this.indexOf('opacity')>-1){styleRules.opacity=this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];}
+return styleRules;};Element.morph=function(element,style){new Effect.Morph(element,Object.extend({style:style},arguments[2]||{}));return element;};['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom','collectTextNodes','collectTextNodesIgnoreClass','morph'].each(function(f){Element.Methods[f]=Element[f];});Element.Methods.visualEffect=function(element,effect,options){s=effect.gsub(/_/,'-').camelize();effect_class=s.charAt(0).toUpperCase()+s.substring(1);new Effect[effect_class](element,options);return $(element);};Element.addMethods();
\ No newline at end of file
diff --git a/framework/Web/Javascripts/js/compressed/prado.js b/framework/Web/Javascripts/js/compressed/prado.js
index f338cb67..e0299c97 100644
--- a/framework/Web/Javascripts/js/compressed/prado.js
+++ b/framework/Web/Javascripts/js/compressed/prado.js
@@ -1,14 +1,255 @@
 
-var Prototype={Version:'1.50',ScriptFragment:'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',emptyFunction:function(){},K:function(x){return x}}
+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.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){}}
+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(){setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.callback();}finally{this.currentlyExecuting=false;}}}}
+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);};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)){params['_method']=this.method;this.method='post';}
+this.parameters=params;if(params=Hash.toQueryString(params)){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);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:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005)
+headers['Connection']='close';}
+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'){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;if(!F.ElementExtensions){Object.extend(methods,Element.Methods),Object.extend(methods,Element.Methods.Simulated);}
+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();},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)
+return{width:element.offsetWidth,height:element.offsetHeight};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';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;};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);var Selector=Class.create();Selector.prototype={initialize:function(expression){this.expression=expression.strip();this.compileMatcher();},compileMatcher:function(){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+)$/))
+predicate+="= "+mm[1];if(mm=formula.match(/^(\d+)?n(\+(\d+))?/)){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:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,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:new RegExp(/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/)},handlers:{concat:function(a,b){for(var i=0,node;node=b[i];i++)
+a.push(node);return a;},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;},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++;}},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);},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;},tagName:function(nodes,root,tagName,combinator){tagName=tagName.toUpperCase();var results=[],h=Selector.handlers;if(nodes){if(combinator){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);},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+$/)){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+))?$/)){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++){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':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;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){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;}},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){}}}});if(Prototype.Browser.IE)
+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){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;},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;},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{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]||{})
+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(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();var Builder={NODEMAP:{AREA:'map',CAPTION:'table',COL:'table',COLGROUP:'table',LEGEND:'fieldset',OPTGROUP:'select',OPTION:'select',PARAM:'object',TBODY:'table',TD:'table',TFOOT:'table',TH:'table',THEAD:'table',TR:'table'},node:function(elementName){elementName=elementName.toUpperCase();var parentTag=this.NODEMAP[elementName]||'div';var parentElement=document.createElement(parentTag);try{parentElement.innerHTML="<"+elementName+"></"+elementName+">";}catch(e){}
+var element=parentElement.firstChild||null;if(element&&(element.tagName.toUpperCase()!=elementName))
+element=element.getElementsByTagName(elementName)[0];if(!element)element=document.createElement(elementName);if(!element)return;if(arguments[1])
+if(this._isStringOrNumber(arguments[1])||(arguments[1]instanceof Array)){this._children(element,arguments[1]);}else{var attrs=this._attributes(arguments[1]);if(attrs.length){try{parentElement.innerHTML="<"+elementName+" "+
+attrs+"></"+elementName+">";}catch(e){}
+element=parentElement.firstChild||null;if(!element){element=document.createElement(elementName);for(attr in arguments[1])
+element[attr=='class'?'className':attr]=arguments[1][attr];}
+if(element.tagName.toUpperCase()!=elementName)
+element=parentElement.getElementsByTagName(elementName)[0];}}
+if(arguments[2])
+this._children(element,arguments[2]);return element;},_text:function(text){return document.createTextNode(text);},ATTR_MAP:{'className':'class','htmlFor':'for'},_attributes:function(attributes){var attrs=[];for(attribute in attributes)
+attrs.push((attribute in this.ATTR_MAP?this.ATTR_MAP[attribute]:attribute)+'="'+attributes[attribute].toString().escapeHTML()+'"');return attrs.join(" ");},_children:function(element,children){if(typeof children=='object'){children.flatten().each(function(e){if(typeof e=='object')
+element.appendChild(e)
+else
+if(Builder._isStringOrNumber(e))
+element.appendChild(Builder._text(e));});}else
+if(Builder._isStringOrNumber(children))
+element.appendChild(Builder._text(children));},_isStringOrNumber:function(param){return(typeof param=='string'||typeof param=='number');},build:function(html){var element=this.node('div');$(element).update(html.strip());return element.down();},dump:function(scope){if(typeof scope!='object'&&typeof scope!='function')scope=window;var tags=("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY "+"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET "+"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);tags.each(function(tag){scope[tag]=function(){return Builder.node.apply(Builder,[tag].concat($A(arguments)));}});}}
 Function.prototype.bindEvent=function()
 {var __method=this,args=$A(arguments),object=args.shift();return function(event)
 {return __method.apply(object,[event||window.event].concat(args));}}
@@ -18,11 +259,7 @@ Object.extend(component.prototype,definition);return component;}
 var Base=function(){if(arguments.length){if(this==window){Base.prototype.extend.call(arguments[0],arguments.callee.prototype);}else{this.extend(arguments[0]);}}};Base.version="1.0.2";Base.prototype={extend:function(source,value){var extend=Base.prototype.extend;if(arguments.length==2){var ancestor=this[source];if((ancestor instanceof Function)&&(value instanceof Function)&&ancestor.valueOf()!=value.valueOf()&&/\bbase\b/.test(value)){var method=value;value=function(){var previous=this.base;this.base=ancestor;var returnValue=method.apply(this,arguments);this.base=previous;return returnValue;};value.valueOf=function(){return method;};value.toString=function(){return String(method);};}
 return this[source]=value;}else if(source){var _prototype={toSource:null};var _protected=["toString","valueOf"];if(Base._prototyping)_protected[2]="constructor";for(var i=0;(name=_protected[i]);i++){if(source[name]!=_prototype[name]){extend.call(this,name,source[name]);}}
 for(var name in source){if(!_prototype[name]){extend.call(this,name,source[name]);}}}
-return this;},base:function(){}};Base.extend=function(_instance,_static){var extend=Base.prototype.extend;if(!_instance)_instance={};Base._prototyping=true;var _prototype=new this;extend.call(_prototype,_instance);var constructor=_prototype.constructor;_prototype.constructor=this;delete Base._prototyping;var klass=function(){if(!Base._prototyping)constructor.apply(this,arguments);this.constructor=klass;};klass.prototype=_prototype;klass.extend=this.extend;klass.implement=this.implement;klass.toString=function(){return String(constructor);};extend.call(klass,_static);var object=constructor?klass:_prototype;if(object.init instanceof Function)object.init();return object;};Base.implement=function(_interface){if(_interface instanceof Function)_interface=_interface.prototype;this.prototype.extend(_interface);};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+=(replacement(match)||'').toString();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 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(/\\/g,'\\\\').replace(/'/g,'\\\'')+"'";}});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;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+(object[match[3]]||'').toString();});}}
-Object.extend(String.prototype,{pad:function(side,len,chr){if(!chr)chr=' ';var s=this;var left=side.toLowerCase()=='left';while(s.length<len)s=left?chr+s:s+chr;return s;},padLeft:function(len,chr){return this.pad('left',len,chr);},padRight:function(len,chr){return this.pad('right',len,chr);},zerofill:function(len){return this.padLeft(len,'0');},trim:function(){return this.replace(/^\s+|\s+$/g,'');},trimLeft:function(){return this.replace(/^\s+/,'');},trimRight:function(){return this.replace(/\s+$/,'');},toFunction:function()
+return this;},base:function(){}};Base.extend=function(_instance,_static){var extend=Base.prototype.extend;if(!_instance)_instance={};Base._prototyping=true;var _prototype=new this;extend.call(_prototype,_instance);var constructor=_prototype.constructor;_prototype.constructor=this;delete Base._prototyping;var klass=function(){if(!Base._prototyping)constructor.apply(this,arguments);this.constructor=klass;};klass.prototype=_prototype;klass.extend=this.extend;klass.implement=this.implement;klass.toString=function(){return String(constructor);};extend.call(klass,_static);var object=constructor?klass:_prototype;if(object.init instanceof Function)object.init();return object;};Base.implement=function(_interface){if(_interface instanceof Function)_interface=_interface.prototype;this.prototype.extend(_interface);};Object.extend(String.prototype,{pad:function(side,len,chr){if(!chr)chr=' ';var s=this;var left=side.toLowerCase()=='left';while(s.length<len)s=left?chr+s:s+chr;return s;},padLeft:function(len,chr){return this.pad('left',len,chr);},padRight:function(len,chr){return this.pad('right',len,chr);},zerofill:function(len){return this.padLeft(len,'0');},trim:function(){return this.replace(/^\s+|\s+$/g,'');},trimLeft:function(){return this.replace(/^\s+/,'');},trimRight:function(){return this.replace(/\s+$/,'');},toFunction:function()
 {var commands=this.split(/\./);var command=window;commands.each(function(action)
 {if(command[new String(action)])
 command=command[new String(action)];});if(typeof(command)=="function")
@@ -38,96 +275,7 @@ return null;m[1]=m[1]||"";m[2]=m[2]||"0";m[4]=m[4]||"0";var cleanInput=m[1]+(m[2
 +"\\s*$");var m=this.match(exp);if(m==null)
 return null;var intermed=m[2]+m[5];var cleanInput=m[1]+intermed.replace(new RegExp("(\\"+groupchar+")","g"),"")
 +((digits>0)?"."+m[7]:"");var num=parseFloat(cleanInput);return(isNaN(num)?null:num);},toDate:function(format)
-{return Date.SimpleParse(this,format);}});var $break=new Object();var $continue=new Object();var Enumerable={each:function(iterator){var index=0;try{this._each(function(value){try{iterator(value,index++);}catch(e){if(e!=$continue)throw e;}});}catch(e){if(e!=$break)throw e;}},all:function(iterator){var result=true;this.each(function(value,index){result=result&&!!(iterator||Prototype.K)(value,index);if(!result)throw $break;});return result;},any:function(iterator){var result=true;this.each(function(value,index){if(result=!!(iterator||Prototype.K)(value,index))
-throw $break;});return result;},collect:function(iterator){var results=[];this.each(function(value,index){results.push(iterator(value,index));});return results;},detect:function(iterator){var result;this.each(function(value,index){if(iterator(value,index)){result=value;throw $break;}});return result;},findAll:function(iterator){var results=[];this.each(function(value,index){if(iterator(value,index))
-results.push(value);});return results;},grep:function(pattern,iterator){var results=[];this.each(function(value,index){var stringValue=value.toString();if(stringValue.match(pattern))
-results.push((iterator||Prototype.K)(value,index));})
-return results;},include:function(object){var found=false;this.each(function(value){if(value==object){found=true;throw $break;}});return found;},inject:function(memo,iterator){this.each(function(value,index){memo=iterator(memo,value,index);});return memo;},invoke:function(method){var args=$A(arguments).slice(1);return this.collect(function(value){return value[method].apply(value,args);});},max:function(iterator){var result;this.each(function(value,index){value=(iterator||Prototype.K)(value,index);if(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.collect(function(value,index){return{value:value,criteria:iterator(value,index)};}).sort(function(left,right){var a=left.criteria,b=right.criteria;return a<b?-1:a>b?1:0;}).pluck('value');},toArray:function(){return this.collect(Prototype.K);},zip:function(){var iterator=Prototype.K,args=$A(arguments);if(typeof args.last()=='function')
-iterator=args.pop();var collections=[this].concat(args).map($A);return this.map(function(value,index){return iterator(collections.pluck(index));});},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);if(!Array.prototype._reverse)
-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&&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();},inspect:function(){return'['+this.map(Object.inspect).join(', ')+']';}});var Hash={_each:function(iterator){for(var 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)
-{if(typeof(pair[1])=='object'||typeof(pair[1])=='array')
-{return $A(pair[1]).collect(function(value)
-{return encodeURIComponent(pair[0])+'='+encodeURIComponent(value);}).join('&');}
-else
-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);}
-function $(){var results=[],element;for(var i=0;i<arguments.length;i++){element=arguments[i];if(typeof element=='string')
-element=document.getElementById(element);results.push(Element.extend(element));}
-return results.length<2?results[0]:results;}
-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(Element.extend(child));return elements;});}
-if(!window.Element)
-var Element=new Object();Element.extend=function(element){if(!element)return;if(_nativeExtensions)return element;if(!element._extended&&element.tagName&&element!=window){var methods=Element.Methods,cache=Element.extend.cache;for(property in methods){var value=methods[property];if(typeof value=='function')
-element[property]=cache.findOrStore(value);}}
-element._extended=true;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(){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);},replace:function(element,html){element=$(element);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);},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*$/);},childOf:function(element,ancestor){element=$(element),ancestor=$(ancestor);while(element=element.parentNode)
-if(element==ancestor)return true;return false;},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(var 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;}}
-Object.extend(Element,Element.Methods);var _nativeExtensions=false;if(!HTMLElement&&/Konqueror|Safari|KHTML/.test(navigator.userAgent)){var HTMLElement={}
-HTMLElement.prototype=document.createElement('div').__proto__;}
-Element.addMethods=function(methods){Object.extend(Element.Methods,methods||{});if(typeof HTMLElement!='undefined'){var methods=Element.Methods,cache=Element.extend.cache;for(property in methods){var value=methods[property];if(typeof value=='function')
-HTMLElement.prototype[property]=cache.findOrStore(value);}
-_nativeExtensions=true;}}
-Element.addMethods();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){var tagName=this.element.tagName.toLowerCase();if(tagName=='tbody'||tagName=='tr'){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(var 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){if(typeof(element.type)=="undefined")
-return false;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||opt.text;}
-return[element.name,value];},selectMany:function(element){var value=[];for(var i=0;i<element.length;i++){var opt=element.options[i];if(opt.selected)
-value.push(opt.value||opt.text);}
-return[element.name,value];}}
-var $F=Form.Element.getValue;Abstract.TimedObserver=function(){}
-Abstract.TimedObserver.prototype={initialize:function(element,frequency,callback){this.frequency=frequency;this.element=$(element);this.callback=callback;this.lastValue=this.getValue();this.registerCallback();},registerCallback:function(){setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},onTimerEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}}}
-Form.Element.Observer=Class.create();Form.Element.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.Observer=Class.create();Form.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.serialize(this.element);}});Abstract.EventObserver=function(){}
-Abstract.EventObserver.prototype={initialize:function(element,callback){this.element=$(element);this.callback=callback;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=='form')
-this.registerFormCallbacks();else
-this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){var elements=Form.getElements(this.element);for(var i=0;i<elements.length;i++)
-this.registerCallback(elements[i]);},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':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,KEY_SPACEBAR:32,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);}}});if(navigator.appVersion.match(/\bMSIE\b/))
-Event.observe(window,'unload',Event.unloadCache,false);Object.extend(Event,{OnLoad:function(fn)
+{return Date.SimpleParse(this,format);}});Object.extend(Event,{OnLoad:function(fn)
 {var w=document.addEventListener&&!window.addEventListener?document:window;Event.observe(w,'load',fn);},keyCode:function(e)
 {return e.keyCode!=null?e.keyCode:e.charCode},isHTMLEvent:function(type)
 {var events=['abort','blur','change','error','focus','load','reset','resize','scroll','select','submit','unload'];return events.include(type);},isMouseEvent:function(type)
@@ -145,62 +293,7 @@ element.dispatchEvent(event);}
 else if(document.createEventObject)
 {var evObj=document.createEventObject();element.fireEvent('on'+type,evObj);}
 else if(typeof(element['on'+type])=="function")
-element['on'+type]();}});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];}}
-var Selector=Class.create();Selector.prototype={initialize:function(expression){this.params={classNames:[]};this.expression=expression.toString().strip();this.parseExpression();this.compileMatcher();},parseExpression:function(){function abort(message){throw'Parse error in selector: '+message;}
-if(this.expression=='')abort('empty expression');var params=this.params,expr=this.expression,match,modifier,clause,rest;while(match=expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)){params.attributes=params.attributes||[];params.attributes.push({name:match[2],operator:match[3],value:match[4]||match[5]||''});expr=match[1];}
-if(expr=='*')return this.params.wildcard=true;while(match=expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)){modifier=match[1],clause=match[2],rest=match[3];switch(modifier){case'#':params.id=clause;break;case'.':params.classNames.push(clause);break;case'':case undefined:params.tagName=clause.toUpperCase();break;default:abort(expr.inspect());}
-expr=rest;}
-if(expr.length>0)abort(expr.inspect());},buildMatchExpression:function(){var params=this.params,conditions=[],clause;if(params.wildcard)
-conditions.push('true');if(clause=params.id)
-conditions.push('element.id == '+clause.inspect());if(clause=params.tagName)
-conditions.push('element.tagName.toUpperCase() == '+clause.inspect());if((clause=params.classNames).length>0)
-for(var i=0;i<clause.length;i++)
-conditions.push('Element.hasClassName(element, '+clause[i].inspect()+')');if(clause=params.attributes){clause.each(function(attribute){var value='element.getAttribute('+attribute.name.inspect()+')';var splitValueBy=function(delimiter){return value+' && '+value+'.split('+delimiter.inspect()+')';}
-switch(attribute.operator){case'=':conditions.push(value+' == '+attribute.value.inspect());break;case'~=':conditions.push(splitValueBy(' ')+'.include('+attribute.value.inspect()+')');break;case'|=':conditions.push(splitValueBy('-')+'.first().toUpperCase() == '+attribute.value.toUpperCase().inspect());break;case'!=':conditions.push(value+' != '+attribute.value.inspect());break;case'':case undefined:conditions.push(value+' != null');break;default:throw'Unknown operator '+attribute.operator+' in selector';}});}
-return conditions.join(' && ');},compileMatcher:function(){this.match=new Function('element','if (!element.tagName) return false; \
-      return '+this.buildMatchExpression());},findElements:function(scope){var element;if(element=$(this.params.id))
-if(this.match(element))
-if(!scope||Element.childOf(element,scope))
-return[element];scope=(scope||document).getElementsByTagName(this.params.tagName||'*');var results=[];for(var i=0;i<scope.length;i++)
-if(this.match(element=scope[i]))
-results.push(Element.extend(element));return results;},toString:function(){return this.expression;}}
-function $$(){return $A(arguments).map(function(expression){return expression.strip().split(/\s+/).inject([null],function(results,expr){var selector=new Selector(expr);return results.map(selector.findElements.bind(selector)).flatten();});}).flatten();}
-var Builder={NODEMAP:{AREA:'map',CAPTION:'table',COL:'table',COLGROUP:'table',LEGEND:'fieldset',OPTGROUP:'select',OPTION:'select',PARAM:'object',TBODY:'table',TD:'table',TFOOT:'table',TH:'table',THEAD:'table',TR:'table'},node:function(elementName){elementName=elementName.toUpperCase();var parentTag=this.NODEMAP[elementName]||'div';var parentElement=document.createElement(parentTag);try{parentElement.innerHTML="<"+elementName+"></"+elementName+">";}catch(e){}
-var element=parentElement.firstChild||null;if(element&&(element.tagName!=elementName))
-element=element.getElementsByTagName(elementName)[0];if(!element)element=document.createElement(elementName);if(!element)return;if(arguments[1])
-if(this._isStringOrNumber(arguments[1])||(arguments[1]instanceof Array)){this._children(element,arguments[1]);}else{var attrs=this._attributes(arguments[1]);if(attrs.length){try{parentElement.innerHTML="<"+elementName+" "+
-attrs+"></"+elementName+">";}catch(e){}
-element=parentElement.firstChild||null;if(!element){element=document.createElement(elementName);for(attr in arguments[1])
-element[attr=='class'?'className':attr]=arguments[1][attr];}
-if(element.tagName!=elementName)
-element=parentElement.getElementsByTagName(elementName)[0];}}
-if(arguments[2])
-this._children(element,arguments[2]);return element;},_text:function(text){return document.createTextNode(text);},_attributes:function(attributes){var attrs=[];for(attribute in attributes)
-attrs.push((attribute=='className'?'class':attribute)+'="'+attributes[attribute].toString().escapeHTML()+'"');return attrs.join(" ");},_children:function(element,children){if(typeof children=='object'){children.flatten().each(function(e){if(typeof e=='object')
-element.appendChild(e)
-else
-if(Builder._isStringOrNumber(e))
-element.appendChild(Builder._text(e));});}else
-if(Builder._isStringOrNumber(children))
-element.appendChild(Builder._text(children));},_isStringOrNumber:function(param){return(typeof param=='string'||typeof param=='number');}}
-Object.extend(Builder,{exportTags:function()
-{var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG","CAPTION"];tags.each(function(tag)
-{window[tag]=function()
-{var args=$A(arguments);if(args.length==0)
-return Builder.node(tag,null);if(args.length==1)
-return Builder.node(tag,args[0]);if(args.length>1)
-return Builder.node(tag,args.shift(),args);};});}});Builder.exportTags();Object.extend(Date.prototype,{SimpleFormat:function(format,data)
+element['on'+type]();}});Object.extend(Date.prototype,{SimpleFormat:function(format,data)
 {data=data||{};var bits=new Array();bits['d']=this.getDate();bits['dd']=String(this.getDate()).zerofill(2);bits['M']=this.getMonth()+1;bits['MM']=String(this.getMonth()+1).zerofill(2);if(data.AbbreviatedMonthNames)
 bits['MMM']=data.AbbreviatedMonthNames[this.getMonth()];if(data.MonthNames)
 bits['MMMM']=data.MonthNames[this.getMonth()];var yearStr=""+this.getFullYear();yearStr=(yearStr.length==2)?'19'+yearStr:yearStr;bits['yyyy']=yearStr;bits['yy']=bits['yyyy'].toString().substr(2,2);var frm=new String(format);for(var sect in bits)
@@ -240,7 +333,13 @@ if(month==2)
 else{if(date>28){return null;}}}
 if((month==4)||(month==6)||(month==9)||(month==11))
 {if(date>30){return null;}}
-var newdate=new Date(year,month-1,date,0,0,0);return newdate;}});var Prado={Version:'3.1',Browser:function()
+var newdate=new Date(year,month-1,date,0,0,0);return newdate;}});Object.extend(Builder,{exportTags:function()
+{var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG","CAPTION"];tags.each(function(tag)
+{window[tag]=function()
+{var args=$A(arguments);if(args.length==0)
+return Builder.node(tag,null);if(args.length==1)
+return Builder.node(tag,args[0]);if(args.length>1)
+return Builder.node(tag,args.shift(),args);};});}});Builder.exportTags();var Prado={Version:'3.1',Browser:function()
 {var info={Version:"1.0"};var is_major=parseInt(navigator.appVersion);info.nver=is_major;info.ver=navigator.appVersion;info.agent=navigator.userAgent;info.dom=document.getElementById?1:0;info.opera=window.opera?1:0;info.ie5=(info.ver.indexOf("MSIE 5")>-1&&info.dom&&!info.opera)?1:0;info.ie6=(info.ver.indexOf("MSIE 6")>-1&&info.dom&&!info.opera)?1:0;info.ie4=(document.all&&!info.dom&&!info.opera)?1:0;info.ie=info.ie4||info.ie5||info.ie6;info.mac=info.agent.indexOf("Mac")>-1;info.ns6=(info.dom&&parseInt(info.ver)>=5)?1:0;info.ie3=(info.ver.indexOf("MSIE")&&(is_major<4));info.hotjava=(info.agent.toLowerCase().indexOf('hotjava')!=-1)?1:0;info.ns4=(document.layers&&!info.dom&&!info.hotjava)?1:0;info.bw=(info.ie6||info.ie5||info.ie4||info.ns4||info.ns6||info.opera);info.ver3=(info.hotjava||info.ie3);info.opera7=((info.agent.toLowerCase().indexOf('opera 7')>-1)||(info.agent.toLowerCase().indexOf('opera/7')>-1));info.operaOld=info.opera&&!info.opera7;return info;},ImportCss:function(doc,css_file)
 {if(Prado.Browser().ie)
 var styleSheet=doc.createStyleSheet(css_file);else
@@ -402,4 +501,34 @@ window.clipboardData.setData("Text",text);},hover:function(obj)
 {for(var i=0;i<options.ItemCount;i++)
 {var checkBoxOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TCheckBox(checkBoxOptions);}}});Prado.WebUI.TRadioButtonList=Base.extend({constructor:function(options)
 {for(var i=0;i<options.ItemCount;i++)
-{var radioButtonOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TRadioButton(radioButtonOptions);}}});
\ No newline at end of file
+{var radioButtonOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TRadioButton(radioButtonOptions);}}});Prado.WebUI.TRatingList=Base.extend({selectedIndex:-1,rating:-1,enabled:true,readOnly:false,constructor:function(options)
+{var cap=$(options.CaptionID);this.options=Object.extend({caption:cap?cap.innerHTML:''},options||{});Prado.WebUI.TRatingList.register(this);this._init();this.selectedIndex=options.SelectedIndex;this.rating=options.Rating;if(options.Rating<=0&&options.SelectedIndex>=0)
+this.rating=options.SelectedIndex+1;this.showRating(this.rating);},_init:function(options)
+{Element.addClassName($(this.options.ListID),this.options.Style);this.radios=new Array();var index=0;for(var i=0;i<this.options.ItemCount;i++)
+{var radio=$(this.options.ListID+'_c'+i);var td=radio.parentNode;if(radio&&td.tagName.toLowerCase()=='td')
+{this.radios.push(radio);Event.observe(td,"mouseover",this.hover.bindEvent(this,index));Event.observe(td,"mouseout",this.recover.bindEvent(this,index));Event.observe(td,"click",this.click.bindEvent(this,index));index++;Element.addClassName(td,"rating");}}},hover:function(ev,index)
+{if(this.enabled==false)return;for(var i=0;i<this.radios.length;i++)
+{var node=this.radios[i].parentNode;var action=i<=index?'addClassName':'removeClassName'
+Element[action](node,"rating_hover");Element.removeClassName(node,"rating_selected");Element.removeClassName(node,"rating_half");}
+this.showCaption(this.getIndexCaption(index));},recover:function(ev,index)
+{if(this.enabled==false)return;this.showRating(this.rating);this.showCaption(this.options.caption);},click:function(ev,index)
+{if(this.enabled==false)return;for(var i=0;i<this.radios.length;i++)
+this.radios[i].checked=(i==index);this.selectedIndex=index;this.setRating(index+1);this.dispatchRequest(ev);},dispatchRequest:function(ev)
+{var requestOptions=Object.extend({ID:this.options.ListID+"_c"+this.selectedIndex,EventTarget:this.options.ListName+"$c"+this.selectedIndex},this.options);var request=new Prado.CallbackRequest(requestOptions.EventTarget,requestOptions);if(request.dispatch()==false)
+Event.stop(ev);},setRating:function(value)
+{this.rating=value;var base=Math.floor(value-1);var remainder=value-base-1;var halfMax=this.options.HalfRating["1"];var index=remainder>halfMax?base+1:base;for(var i=0;i<this.radios.length;i++)
+this.radios[i].checked=(i==index);var caption=this.getIndexCaption(index);this.setCaption(caption);this.showCaption(caption);this.showRating(value);},showRating:function(value)
+{var base=Math.floor(value-1);var remainder=value-base-1;var halfMin=this.options.HalfRating["0"];var halfMax=this.options.HalfRating["1"];var index=remainder>halfMax?base+1:base;var hasHalf=remainder>=halfMin&&remainder<=halfMax;for(var i=0;i<this.radios.length;i++)
+{var node=this.radios[i].parentNode;var action=i>index?'removeClassName':'addClassName';Element[action](node,"rating_selected");if(i==index+1&&hasHalf)
+Element.addClassName(node,"rating_half");else
+Element.removeClassName(node,"rating_half");Element.removeClassName(node,"rating_hover");}},getIndexCaption:function(index)
+{return index>-1?this.radios[index].value:this.options.caption;},showCaption:function(value)
+{var caption=$(this.options.CaptionID);if(caption)caption.innerHTML=value;$(this.options.ListID).title=value;},setCaption:function(value)
+{this.options.caption=value;this.showCaption(value);},setEnabled:function(value)
+{this.enabled=value;for(var i=0;i<this.radios.length;i++)
+{var action=value?'removeClassName':'addClassName'
+Element[action](this.radios[i].parentNode,"rating_disabled");}}},{ratings:{},register:function(rating)
+{Prado.WebUI.TRatingList.ratings[rating.options.ListID]=rating;},setEnabled:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setEnabled(value);},setRating:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setRating(value);},setCaption:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setCaption(value);}});
\ No newline at end of file
diff --git a/framework/Web/Javascripts/js/debug/effects.js b/framework/Web/Javascripts/js/debug/effects.js
index c0f3c4af..685e57ae 100644
--- a/framework/Web/Javascripts/js/debug/effects.js
+++ b/framework/Web/Javascripts/js/debug/effects.js
@@ -1,15 +1,18 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
 // Contributors:
 //  Justin Palmer (http://encytemedia.com/)
 //  Mark Pilgrim (http://diveintomark.org/)
 //  Martin Bialasinki
 // 
-// See scriptaculous.js for full license.  
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
 
 // converts rgb() and #xxx to #xxxxxx format,  
 // returns self (or first argument) if not convertable  
 String.prototype.parseColor = function() {  
-  var color = '#';  
+  var color = '#';
   if(this.slice(0,4) == 'rgb(') {  
     var cols = this.slice(4,this.length-1).split(',');  
     var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
@@ -41,48 +44,21 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
 
 Element.setContentZoom = function(element, percent) {
   element = $(element);  
-  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  element.setStyle({fontSize: (percent/100) + 'em'});   
   if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+  return element;
 }
 
-Element.getOpacity = function(element){  
-  var opacity;
-  if (opacity = Element.getStyle(element, 'opacity'))  
-    return parseFloat(opacity);  
-  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
-    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
-  return 1.0;  
+Element.getOpacity = function(element){
+  return $(element).getStyle('opacity');
 }
 
-Element.setOpacity = function(element, value){  
-  element= $(element);  
-  if (value == 1){
-    Element.setStyle(element, { opacity: 
-      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
-      0.999999 : null });
-    if(/MSIE/.test(navigator.userAgent))  
-      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
-  } else {  
-    if(value < 0.00001) value = 0;  
-    Element.setStyle(element, {opacity: value});
-    if(/MSIE/.test(navigator.userAgent))  
-     Element.setStyle(element, 
-       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
-                 'alpha(opacity='+value*100+')' });  
-  }
-}  
- 
-Element.getInlineOpacity = function(element){  
-  return $(element).style.opacity || '';
-}  
+Element.setOpacity = function(element, value){
+  return $(element).setStyle({opacity:value});
+}
 
-Element.childrenWithClassName = function(element, className, findFirst) {
-  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
-  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
-    return (c.className && c.className.match(classNameRegExp));
-  });
-  if(!results) results = [];
-  return results;
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
 }
 
 Element.forceRerendering = function(element) {
@@ -104,12 +80,17 @@ Array.prototype.call = function() {
 /*--------------------------------------------------------------------------*/
 
 var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
   tagifyText: function(element) {
     if(typeof Builder == 'undefined')
       throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
       
     var tagifyStyle = 'position:relative';
-    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
+    
     element = $(element);
     $A(element.childNodes).each( function(child) {
       if(child.nodeType==3) {
@@ -162,32 +143,35 @@ var Effect2 = Effect; // deprecated
 
 /* ------------- transitions ------------- */
 
-Effect.Transitions = {}
-
-Effect.Transitions.linear = Prototype.K;
-
-Effect.Transitions.sinoidal = function(pos) {
-  return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse  = function(pos) {
-  return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
-  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
-  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
-  return (Math.floor(pos*10) % 2 == 0 ? 
-    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
-  return 0;
-}
-Effect.Transitions.full = function(pos) {
-  return 1;
-}
+Effect.Transitions = {
+  linear: Prototype.K,
+  sinoidal: function(pos) {
+    return (-Math.cos(pos*Math.PI)/2) + 0.5;
+  },
+  reverse: function(pos) {
+    return 1-pos;
+  },
+  flicker: function(pos) {
+    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+  },
+  wobble: function(pos) {
+    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+  },
+  pulse: function(pos, pulses) { 
+    pulses = pulses || 5; 
+    return (
+      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
+            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
+        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+      );
+  },
+  none: function(pos) {
+    return 0;
+  },
+  full: function(pos) {
+    return 1;
+  }
+};
 
 /* ------------- core effects ------------- */
 
@@ -214,6 +198,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
             e.finishOn += effect.finishOn;
           });
         break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
       case 'end':
         // start effect after last queued effect has finished
         timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -227,7 +214,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
       this.effects.push(effect);
     
     if(!this.interval) 
-      this.interval = setInterval(this.loop.bind(this), 40);
+      this.interval = setInterval(this.loop.bind(this), 15);
   },
   remove: function(effect) {
     this.effects = this.effects.reject(function(e) { return e==effect });
@@ -238,7 +225,8 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
   },
   loop: function() {
     var timePos = new Date().getTime();
-    this.effects.invoke('loop', timePos);
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      if(this.effects[i]) this.effects[i].loop(timePos);
   }
 });
 
@@ -258,7 +246,7 @@ Effect.Queue = Effect.Queues.get('global');
 Effect.DefaultOptions = {
   transition: Effect.Transitions.sinoidal,
   duration:   1.0,   // seconds
-  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+  fps:        60.0,  // max. 60fps due to Effect.Queue implementation
   sync:       false, // true for combining
   from:       0.0,
   to:         1.0,
@@ -326,7 +314,10 @@ Effect.Base.prototype = {
     if(this.options[eventName]) this.options[eventName](this);
   },
   inspect: function() {
-    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+    var data = $H();
+    for(property in this)
+      if(typeof this[property] != 'function') data[property] = this[property];
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
   }
 }
 
@@ -350,12 +341,24 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
   }
 });
 
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+  initialize: function() {
+    var options = Object.extend({
+      duration: 0
+    }, arguments[0] || {});
+    this.start(options);
+  },
+  update: Prototype.emptyFunction
+});
+
 Effect.Opacity = Class.create();
 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     // make this work on IE on elements without 'layout'
-    if(/MSIE/.test(navigator.userAgent) && (!this.element.currentStyle.hasLayout))
+    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
       this.element.setStyle({zoom: 1});
     var options = Object.extend({
       from: this.element.getOpacity() || 0.0,
@@ -372,6 +375,7 @@ Effect.Move = Class.create();
 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       x:    0,
       y:    0,
@@ -410,7 +414,8 @@ Effect.MoveBy = function(element, toTop, toLeft) {
 Effect.Scale = Class.create();
 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
   initialize: function(element, percent) {
-    this.element = $(element)
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       scaleX: true,
       scaleY: true,
@@ -460,7 +465,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
     this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
   },
   finish: function(position) {
-    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+    if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
   },
   setDimensions: function(height, width) {
     var d = {};
@@ -485,6 +490,7 @@ Effect.Highlight = Class.create();
 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
     this.start(options);
   },
@@ -492,9 +498,11 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
     // Prevent executing on elements not in the layout flow
     if(this.element.getStyle('display')=='none') { this.cancel(); return; }
     // Disable background image during the effect
-    this.oldStyle = {
-      backgroundImage: this.element.getStyle('background-image') };
-    this.element.setStyle({backgroundImage: 'none'});
+    this.oldStyle = {};
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
     if(!this.options.endcolor)
       this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
     if(!this.options.restorecolor)
@@ -549,8 +557,7 @@ Effect.Fade = function(element) {
   to:   0.0,
   afterFinishInternal: function(effect) { 
     if(effect.options.to!=0) return;
-    effect.element.hide();
-    effect.element.setStyle({opacity: oldOpacity}); 
+    effect.element.hide().setStyle({opacity: oldOpacity}); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
@@ -565,25 +572,31 @@ Effect.Appear = function(element) {
     effect.element.forceRerendering();
   },
   beforeSetup: function(effect) {
-    effect.element.setOpacity(effect.options.from);
-    effect.element.show(); 
+    effect.element.setOpacity(effect.options.from).show(); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
 
 Effect.Puff = function(element) {
   element = $(element);
-  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
   return new Effect.Parallel(
    [ new Effect.Scale(element, 200, 
       { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
      Object.extend({ duration: 1.0, 
       beforeSetupInternal: function(effect) {
-        effect.effects[0].element.setStyle({position: 'absolute'}); },
+        Position.absolutize(effect.effects[0].element)
+      },
       afterFinishInternal: function(effect) {
-         effect.effects[0].element.hide();
-         effect.effects[0].element.setStyle(oldStyle); }
+         effect.effects[0].element.hide().setStyle(oldStyle); }
      }, arguments[1] || {})
    );
 }
@@ -596,8 +609,7 @@ Effect.BlindUp = function(element) {
       scaleX: false, 
       restoreAfterFinish: true,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping();
+        effect.element.hide().undoClipping();
       } 
     }, arguments[1] || {})
   );
@@ -613,9 +625,7 @@ Effect.BlindDown = function(element) {
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
     restoreAfterFinish: true,
     afterSetup: function(effect) {
-      effect.element.makeClipping();
-      effect.element.setStyle({height: '0px'});
-      effect.element.show(); 
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
     },  
     afterFinishInternal: function(effect) {
       effect.element.undoClipping();
@@ -635,14 +645,10 @@ Effect.SwitchOff = function(element) {
         duration: 0.3, scaleFromCenter: true,
         scaleX: false, scaleContent: false, restoreAfterFinish: true,
         beforeSetup: function(effect) { 
-          effect.element.makePositioned();
-          effect.element.makeClipping();
+          effect.element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
-          effect.element.hide();
-          effect.element.undoClipping();
-          effect.element.undoPositioned();
-          effect.element.setStyle({opacity: oldOpacity});
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
         }
       })
     }
@@ -664,9 +670,7 @@ Effect.DropOut = function(element) {
           effect.effects[0].element.makePositioned(); 
         },
         afterFinishInternal: function(effect) {
-          effect.effects[0].element.hide();
-          effect.effects[0].element.undoPositioned();
-          effect.effects[0].element.setStyle(oldStyle);
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
         } 
       }, arguments[1] || {}));
 }
@@ -688,16 +692,14 @@ Effect.Shake = function(element) {
       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     new Effect.Move(effect.element,
       { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
-        effect.element.undoPositioned();
-        effect.element.setStyle(oldStyle);
+        effect.element.undoPositioned().setStyle(oldStyle);
   }}) }}) }}) }}) }}) }});
 }
 
 Effect.SlideDown = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
+  element = $(element).cleanWhitespace();
   // SlideDown need to have the content of the element wrapped in a container element with fixed height!
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var oldInnerBottom = element.down().getStyle('bottom');
   var elementDimensions = element.getDimensions();
   return new Effect.Scale(element, 100, Object.extend({ 
     scaleContent: false, 
@@ -707,34 +709,24 @@ Effect.SlideDown = function(element) {
     restoreAfterFinish: true,
     afterSetup: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.setStyle({height: '0px'});
-      effect.element.show(); },
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
+      effect.element.down().setStyle({bottom:
         (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
     },
     afterFinishInternal: function(effect) {
-      effect.element.undoClipping(); 
-      // IE will crash if child is undoPositioned first
-      if(/MSIE/.test(navigator.userAgent)){
-        effect.element.undoPositioned();
-        effect.element.firstChild.undoPositioned();
-      }else{
-        effect.element.firstChild.undoPositioned();
-        effect.element.undoPositioned();
-      }
-      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
     }, arguments[1] || {})
   );
 }
 
 Effect.SlideUp = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
   return new Effect.Scale(element, window.opera ? 0 : 1,
    Object.extend({ scaleContent: false, 
     scaleX: false, 
@@ -743,32 +735,32 @@ Effect.SlideUp = function(element) {
     restoreAfterFinish: true,
     beforeStartInternal: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.show(); },  
+      effect.element.makeClipping().show();
+    },  
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
     afterFinishInternal: function(effect) {
-      effect.element.hide();
-      effect.element.undoClipping();
-      effect.element.firstChild.undoPositioned();
-      effect.element.undoPositioned();
-      effect.element.setStyle({bottom: oldInnerBottom}); }
+      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+      effect.element.down().undoPositioned();
+    }
    }, arguments[1] || {})
   );
 }
 
 // Bug in opera makes the TD containing this element expand for a instance after finish 
 Effect.Squish = function(element) {
-  return new Effect.Scale(element, window.opera ? 1 : 0, 
-    { restoreAfterFinish: true,
-      beforeSetup: function(effect) {
-        effect.element.makeClipping(effect.element); },  
-      afterFinishInternal: function(effect) {
-        effect.element.hide(effect.element); 
-        effect.element.undoClipping(effect.element); }
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
   });
 }
 
@@ -824,9 +816,7 @@ Effect.Grow = function(element) {
     y: initialMoveY,
     duration: 0.01, 
     beforeSetup: function(effect) {
-      effect.element.hide();
-      effect.element.makeClipping();
-      effect.element.makePositioned();
+      effect.element.hide().makeClipping().makePositioned();
     },
     afterFinishInternal: function(effect) {
       new Effect.Parallel(
@@ -837,13 +827,10 @@ Effect.Grow = function(element) {
             sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
         ], Object.extend({
              beforeSetup: function(effect) {
-               effect.effects[0].element.setStyle({height: '0px'});
-               effect.effects[0].element.show(); 
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
              },
              afterFinishInternal: function(effect) {
-               effect.effects[0].element.undoClipping();
-               effect.effects[0].element.undoPositioned();
-               effect.effects[0].element.setStyle(oldStyle); 
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
              }
            }, options)
       )
@@ -897,13 +884,10 @@ Effect.Shrink = function(element) {
       new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
     ], Object.extend({            
          beforeStartInternal: function(effect) {
-           effect.effects[0].element.makePositioned();
-           effect.effects[0].element.makeClipping(); },
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
          afterFinishInternal: function(effect) {
-           effect.effects[0].element.hide();
-           effect.effects[0].element.undoClipping();
-           effect.effects[0].element.undoPositioned();
-           effect.effects[0].element.setStyle(oldStyle); }
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
        }, options)
   );
 }
@@ -913,10 +897,10 @@ Effect.Pulsate = function(element) {
   var options    = arguments[1] || {};
   var oldOpacity = element.getInlineOpacity();
   var transition = options.transition || Effect.Transitions.sinoidal;
-  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
   reverser.bind(transition);
   return new Effect.Opacity(element, 
-    Object.extend(Object.extend({  duration: 3.0, from: 0,
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
       afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
     }, options), {transition: reverser}));
 }
@@ -928,7 +912,7 @@ Effect.Fold = function(element) {
     left: element.style.left,
     width: element.style.width,
     height: element.style.height };
-  Element.makeClipping(element);
+  element.makeClipping();
   return new Effect.Scale(element, 5, Object.extend({   
     scaleContent: false,
     scaleX: false,
@@ -937,15 +921,162 @@ Effect.Fold = function(element) {
       scaleContent: false, 
       scaleY: false,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping(); 
-        effect.element.setStyle(oldStyle);
+        effect.element.hide().undoClipping().setStyle(oldStyle);
       } });
   }}, arguments[1] || {}));
 };
 
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: {}
+    }, arguments[1] || {});
+    if (typeof options.style == 'string') {
+      if(options.style.indexOf(':') == -1) {
+        var cssText = '', selector = '.' + options.style;
+        $A(document.styleSheets).reverse().each(function(styleSheet) {
+          if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
+          else if (styleSheet.rules) cssRules = styleSheet.rules;
+          $A(cssRules).reverse().each(function(rule) {
+            if (selector == rule.selectorText) {
+              cssText = rule.style.cssText;
+              throw $break;
+            }
+          });
+          if (cssText) throw $break;
+        });
+        this.style = cssText.parseStyle();
+        options.afterFinishInternal = function(effect){
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            if(transform.style != 'opacity')
+              effect.element.style[transform.style.camelize()] = '';
+          });
+        }
+      } else this.style = options.style.parseStyle();
+    } else this.style = $H(options.style)
+    this.start(options);
+  },
+  setup: function(){
+    function parseColor(color){
+      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0].underscore().dasherize(), value = pair[1], unit = null;
+
+      if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if(property == 'opacity') {
+        value = parseFloat(value);
+        if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if(Element.CSS_LENGTH.test(value)) 
+        var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+
+      var originalValue = this.element.getStyle(property);
+      return $H({ 
+        style: property, 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      });
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = $H(), value = null;
+    this.transforms.each(function(transform){
+      value = transform.unit=='color' ?
+        $R(0,2).inject('#',function(m,v,i){
+          return m+(Math.round(transform.originalValue[i]+
+            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
+        transform.originalValue + Math.round(
+          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+      style[transform.style] = value;
+    });
+    this.element.setStyle(style);
+  }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || {};
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      var data = $H(track).values().first();
+      this.tracks.push($H({
+        ids:     $H(track).keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var elements = [$(track.ids) || $$(track.ids)].flatten();
+        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+  var element = Element.extend(document.createElement('div'));
+  element.innerHTML = '<div style="' + this + '"></div>';
+  var style = element.down().style, styleRules = $H();
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if(style[property]) styleRules[property] = style[property]; 
+  });
+  if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
+    styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
+  }
+  return styleRules;
+};
+
+Element.morph = function(element, style) {
+  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+  return element;
+};
+
 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
   function(f) { Element.Methods[f] = Element[f]; }
 );
 
@@ -958,11 +1089,3 @@ Element.Methods.visualEffect = function(element, effect, options) {
 
 Element.addMethods();
 
-Prado.Effect =
-{
-	Highlight : function(element, options)
-	{
-		new Effect.Highlight(element,options||{});
-	}
-}
-
diff --git a/framework/Web/Javascripts/js/debug/prado.js b/framework/Web/Javascripts/js/debug/prado.js
index 41415d1f..8f292b72 100644
--- a/framework/Web/Javascripts/js/debug/prado.js
+++ b/framework/Web/Javascripts/js/debug/prado.js
@@ -1,31 +1,36 @@
-/*  Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %>
- *  (c) 2005 Sam Stephenson <sam@conio.net>
+/*  Prototype JavaScript framework, version 1.5.1_rc2
+ *  (c) 2005-2007 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
- *  For details, see the Prototype web site: http://prototype.conio.net/
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
  *
 /*--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.50',
+  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}
+  K: function(x) { return x }
 }
 
-/*
-<%= include 'base.js', 'string.js' %>
-
-<%= include 'enumerable.js', 'array.js', 'hash.js', 'range.js' %>
-
-<%= include 'ajax.js', 'dom.js', 'selector.js', 'form.js', 'event.js', 'position.js' %>
-
-*/
-
 var Class = {
   create: function() {
-    return function() { 
+    return function() {
       this.initialize.apply(this, arguments);
     }
   }
@@ -40,16 +45,56 @@ Object.extend = function(destination, source) {
   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;
+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();
@@ -59,34 +104,50 @@ Function.prototype.bind = function() {
 }
 
 Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
+  var __method = this, args = $A(arguments), object = args.shift();
   return function(event) {
-    return __method.call(object, event || window.event);
+    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
   }
 }
 
 Object.extend(Number.prototype, {
   toColorPart: function() {
-    var digits = this.toString(16);
-    if (this < 16) return '0' + digits;
-    return digits;
+    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; i < arguments.length; i++) {
+    for (var i = 0, length = arguments.length; i < length; i++) {
       var lambda = arguments[i];
       try {
         returnValue = lambda();
@@ -111,365 +172,49 @@ PeriodicalExecuter.prototype = {
   },
 
   registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+    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 { 
+      try {
         this.currentlyExecuting = true;
-        this.callback(); 
-      } finally { 
+        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);
 
-
-/**
- * Similar to bindAsEventLister, but takes additional arguments.
- */
-Function.prototype.bindEvent = function()
-{
-	var __method = this, args = $A(arguments), object = args.shift();
-	return function(event)
-	{
-		return __method.apply(object, [event || window.event].concat(args));
-	}
-}
-
-/**
- * Creates a new function by copying function definition from
- * the <tt>base</tt> and optional <tt>definition</tt>.
- * @param function a base function to copy from.
- * @param array additional definition
- * @param function return a new function with definition from both
- * <tt>base</tt> and <tt>definition</tt>.
- */
-Class.extend = function(base, definition)
-{
-		var component = Class.create();
-		Object.extend(component.prototype, base.prototype);
-		if(definition)
-			Object.extend(component.prototype, definition);
-		return component;
-}
-
-/*
-	Base, version 1.0.2
-	Copyright 2006, Dean Edwards
-	License: http://creativecommons.org/licenses/LGPL/2.1/
-*/
-
-var Base = function() {
-	if (arguments.length) {
-		if (this == window) { // cast an object to this class
-			Base.prototype.extend.call(arguments[0], arguments.callee.prototype);
-		} else {
-			this.extend(arguments[0]);
-		}
-	}
-};
-
-Base.version = "1.0.2";
-
-Base.prototype = {
-	extend: function(source, value) {
-		var extend = Base.prototype.extend;
-		if (arguments.length == 2) {
-			var ancestor = this[source];
-			// overriding?
-			if ((ancestor instanceof Function) && (value instanceof Function) &&
-				ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
-				var method = value;
-			//	var _prototype = this.constructor.prototype;
-			//	var fromPrototype = !Base._prototyping && _prototype[source] == ancestor;
-				value = function() {
-					var previous = this.base;
-				//	this.base = fromPrototype ? _prototype[source] : ancestor;
-					this.base = ancestor;
-					var returnValue = method.apply(this, arguments);
-					this.base = previous;
-					return returnValue;
-				};
-				// point to the underlying method
-				value.valueOf = function() {
-					return method;
-				};
-				value.toString = function() {
-					return String(method);
-				};
-			}
-			return this[source] = value;
-		} else if (source) {
-			var _prototype = {toSource: null};
-			// do the "toString" and other methods manually
-			var _protected = ["toString", "valueOf"];
-			// if we are prototyping then include the constructor
-			if (Base._prototyping) _protected[2] = "constructor";
-			for (var i = 0; (name = _protected[i]); i++) {
-				if (source[name] != _prototype[name]) {
-					extend.call(this, name, source[name]);
-				}
-			}
-			// copy each of the source object's properties to this object
-			for (var name in source) {
-				if (!_prototype[name]) {
-					extend.call(this, name, source[name]);
-				}
-			}
-		}
-		return this;
-	},
-
-	base: function() {
-		// call this method from any other method to invoke that method's ancestor
-	}
-};
-
-Base.extend = function(_instance, _static) {
-	var extend = Base.prototype.extend;
-	if (!_instance) _instance = {};
-	// build the prototype
-	Base._prototyping = true;
-	var _prototype = new this;
-	extend.call(_prototype, _instance);
-	var constructor = _prototype.constructor;
-	_prototype.constructor = this;
-	delete Base._prototyping;
-	// create the wrapper for the constructor function
-	var klass = function() {
-		if (!Base._prototyping) constructor.apply(this, arguments);
-		this.constructor = klass;
-	};
-	klass.prototype = _prototype;
-	// build the class interface
-	klass.extend = this.extend;
-	klass.implement = this.implement;
-	klass.toString = function() {
-		return String(constructor);
-	};
-	extend.call(klass, _static);
-	// single instance
-	var object = constructor ? klass : _prototype;
-	// class initialisation
-	if (object.init instanceof Function) object.init();
-	return object;
-};
-
-Base.implement = function(_interface) {
-	if (_interface instanceof Function) _interface = _interface.prototype;
-	this.prototype.extend(_interface);
-};
-
-/*
- * Signals and Slots for Prototype: Easy custom javascript events
- * http://tetlaw.id.au/view/blog/signals-and-slots-for-prototype-easy-custom-javascript-events
- * Andrew Tetlaw
- * Version 1.2 (2006-06-19)
- *
- * http://creativecommons.org/licenses/by-sa/2.5/
- *
-Signal = {
-	throwErrors : true,
-	MT : function(){ return true },
-	connect : function(obj1, func1, obj2, func2, options) {
-		var options = Object.extend({
-			connectOnce : false,
-			before : false,
-			mutate : function() {return arguments;}
-		}, options || {});
-		if(typeof func1 != 'string' || typeof func2 != 'string') return;
-
-		var sigObj = obj1 || window;
-		var slotObj = obj2 || window;
-		var signame = func1+'__signal_';
-		var slotsname = func1+'__slots_';
-		if(!sigObj[signame]) {
-			// having the slotFunc in a var and setting it by using an anonymous function in this way
-			// is apparently a good way to prevent memory leaks in IE if the objects are DOM nodes.
-			var slotFunc = function() {
-				var args = [];
-				for(var x = 0; x < arguments.length; x++){
-					args.push(arguments[x]);
-				}
-				args = options.mutate.apply(null,args)
-				var result;
-				if(!options.before) result = sigObj[signame].apply(sigObj,arguments); //default: call sign before slot
-				sigObj[slotsname].each(function(slot){
-					try {
-						if(slot && slot[0]) { // testing for null, a disconnect may have nulled this slot
-							slot[0][slot[1]].apply(slot[0],args); //[0] = obj, [1] = func name
-						}
-					} catch(e) {
-						if(Signal.throwErrors) throw e;
-					}
-				});
-				if(options.before) result = sigObj[signame].apply(sigObj,arguments); //call slot before sig
-				return result; //return sig result
-			};
-			(function() {
-				sigObj[slotsname] = $A([]);
-				sigObj[signame] = sigObj[func1] || Signal.MT;
-				sigObj[func1] = slotFunc;
-			})();
-		}
-		var con = (sigObj[slotsname].length > 0) ?
-					(options.connectOnce ? !sigObj[slotsname].any(function(slot) { return (slot[0] == slotObj && slot[1] == func2) }) : true) :
-					true;
-		if(con) {
-			sigObj[slotsname].push([slotObj,func2]);
-		}
-	},
-	connectOnce : function(obj1, func1, obj2, func2, options) {
-		Signal.connect(obj1, func1, obj2, func2, Object.extend(options || {}, {connectOnce : true}))
-	},
-	disconnect : function(obj1, func1, obj2, func2, options) {
-		var options = Object.extend({
-			disconnectAll : false
-		}, options || {});
-		if(typeof func1 != 'string' || typeof func2 != 'string') return;
-
-		var sigObj = obj1 || window;
-		var slotObj = obj2 || window;
-		var signame = func1+'__signal_';
-		var slotsname = func1+'__slots_';
-
-		// I null them in this way so that any currectly active signal will read a null slot,
-		// otherwise the slot will be applied even though it's been disconnected
-		if(sigObj[slotsname]) {
-			if(options.disconnectAll) {
-				sigObj[slotsname] = sigObj[slotsname].collect(function(slot) {
-					if(slot[0] == slotObj && slot[1] == func2) {
-						slot[0] = null;
-						return null;
-					} else {
-						return slot;
-					}
-				}).compact();
-			} else {
-				var idx = -1;
-				sigObj[slotsname] = sigObj[slotsname].collect(function(slot, index) {
-					if(slot[0] == slotObj && slot[1] == func2 && idx < 0) {  //disconnect first match
-						idx = index;
-						slot[0] = null;
-						return null;
-					} else {
-						return slot;
-					}
-				}).compact();
-			}
-		}
-	},
-	disconnectAll : function(obj1, func1, obj2, func2, options) {
-		Signal.disconnect(obj1, func1, obj2, func2, Object.extend(options || {}, {disconnectAll : true}))
-	}
-}
-*/
-
-/*
- Tests
-
-//   1. Simple Test 1 "hello Fred" should trigger "Fred is a stupid head"
-
-
-      sayHello = function(n) {
-      	alert("Hello! " + n);
-      }
-      moron = function(n) {
-      	alert(n + " is a stupid head");
-      }
-      Signal.connect(null,'sayHello',null,'moron');
-
-      onclick="sayHello('Fred')"
-
-
-//   2. Simple Test 2 repeated insults about Fred
-
-
-      Signal.connect(null,'sayHello2',null,'moron2');
-      Signal.connect(null,'sayHello2',null,'moron2');
-      Signal.connect(null,'sayHello2',null,'moron2');
-
-
-//   3. Simple Test 3 multiple insults about Fred
-
-
-      Signal.connect(null,'sayHello3',null,'moron3');
-      Signal.connect(null,'sayHello3',null,'bonehead3');
-      Signal.connect(null,'sayHello3',null,'idiot3');
-
-
-//   4. Simple Test 4 3 insults about Fred first - 3 then none
-
-
-      Signal.connect(null,'sayHello4',null,'moron4');
-      Signal.connect(null,'sayHello4',null,'moron4');
-      Signal.connect(null,'sayHello4',null,'moron4');
-      Signal.disconnect(null,'sayHello4',null,'moron4');
-      Signal.disconnect(null,'sayHello4',null,'moron4');
-      Signal.disconnect(null,'sayHello4',null,'moron4');
-
-
-//   5. Simple Test 5 connect 3 insults about Fred first - only one, then none
-
-
-      Signal.connect(null,'sayHello5',null,'moron5');
-      Signal.connect(null,'sayHello5',null,'moron5');
-      Signal.connect(null,'sayHello5',null,'moron5');
-      Signal.disconnectAll(null,'sayHello5',null,'moron5');
-
-
-//   6. Simple Test 6 connect 3 insults but only one comes out
-
-
-      Signal.connectOnce(null,'sayHello6',null,'moron6');
-      Signal.connectOnce(null,'sayHello6',null,'moron6');
-      Signal.connectOnce(null,'sayHello6',null,'moron6');
-
-
-//   7. Simple Test 7 connect via objects
-
-
-      var o = {};
-      o.sayHello = function(n) {
-      	alert("Hello! " + n + " (from object o)");
-      }
-      var m = {};
-      m.moron = function(n) {
-      	alert(n + " is a stupid head (from object m)");
-      }
-
-      Signal.connect(o,'sayHello',m,'moron');
-
-      onclick="o.sayHello('Fred')"
-
-
-//   8. Simple Test 8 connect but the insult comes first using {before:true}
-
-
-      Signal.connect(null,'sayHello8',null,'moron8', {before:true});
-
-
-//   9. Simple Test 9 connect but the insult is mutated
-
-
-      Signal.connect(null,'sayHello9',null,'moron9', {mutate:function() { return ['smelly ' + arguments[0]] }});
-
- */
-
-
-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 += (replacement(match) || '').toString();
+        result += String.interpret(replacement(match));
         source  = source.slice(match.index + match[0].length);
       } else {
         result += source, source = '';
@@ -477,33 +222,33 @@ Object.extend(String.prototype, {
     }
     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 ? 
+    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, '');
   },
@@ -511,7 +256,7 @@ Object.extend(String.prototype, {
   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');
@@ -519,55 +264,125 @@ Object.extend(String.prototype, {
       return (scriptTag.match(matchOne) || ['', ''])[1];
     });
   },
-  
+
   evalScripts: function() {
     return this.extractScripts().map(function(script) { return eval(script) });
   },
 
   escapeHTML: function() {
-    var div = document.createElement('div');
-    var text = document.createTextNode(this);
-    div.appendChild(text);
-    return div.innerHTML;
+    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[0].nodeValue : '';
+    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() {
-    var pairs = this.match(/^\??(.*)$/)[1].split('&');
-    return pairs.inject({}, function(params, pairString) {
-      var pair = pairString.split('=');
-      params[pair[0]] = pair[1];
-      return params;
+
+  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 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;
+    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;
   },
 
-  inspect: function() {
-    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  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);
   }
 });
 
@@ -579,6 +394,13 @@ String.prototype.gsub.prepareReplacement = function(replacement) {
 
 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 = {
@@ -586,209 +408,39 @@ Template.prototype = {
     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 + (object[match[3]] || '').toString();
+      return before + String.interpret(object[match[3]]);
     });
   }
 }
 
-
-/**
- * @class String extensions
- */
-Object.extend(String.prototype, 
-{
-	/**
-	 * @param {String} "left" to pad the string on the left, "right" to pad right.
-	 * @param {Number} minimum string length.
-	 * @param {String} character(s) to pad 
-	 * @return {String} padded character(s) on the left or right to satisfy minimum string length
-	 */
-
-	pad : function(side, len, chr) {
-		if (!chr) chr = ' ';
-		var s = this;
-		var left = side.toLowerCase()=='left';
-		while (s.length<len) s = left? chr + s : s + chr;
-		return s;
-	},
-
-	/**
-	 * @param {Number} minimum string length.
-	 * @param {String} character(s) to pad 
-	 * @return {String} padded character(s) on the left to satisfy minimum string length
-	 */
-	padLeft : function(len, chr) {
-		return this.pad('left',len,chr);
-	},
-
-	/**
-	 * @param {Number} minimum string length.
-	 * @param {String} character(s) to pad 
-	 * @return {String} padded character(s) on the right to satisfy minimum string length
-	 */
-	padRight : function(len, chr) {
-		return this.pad('right',len,chr);
-	},
-
-	/**
-	 * @param {Number} minimum string length.
-	 * @return {String} append zeros to the left to satisfy minimum string length.
-	 */
-	zerofill : function(len) { 
-		return this.padLeft(len,'0');
-	},
-
-	/**
-	 * @return {String} removed white spaces from both ends.
-	 */
-	trim : function() { 
-		return this.replace(/^\s+|\s+$/g,'');
-	},
-
-	/**
-	 * @return {String} removed white spaces from the left end.
-	 */
-	trimLeft : function() { 
-		return this.replace(/^\s+/,''); 
-	},
-
-	/**
-	 * @return {String} removed white spaces from the right end.
-	 */
-	trimRight : function() { 
-		return this.replace(/\s+$/,'');
-	},
-
-	/**
-	 * Convert period separated function names into a function reference.
-	 * e.g. "Prado.AJAX.Callback.Action.setValue".toFunction() will return
-	 * the actual function Prado.AJAX.Callback.Action.setValue()
-	 * @return {Function} the corresponding function represented by the string.
-	 */
-	toFunction : function()
-	{
-		var commands = this.split(/\./);
-		var command = window;
-		commands.each(function(action)
-		{ 
-			if(command[new String(action)]) 
-				command=command[new String(action)]; 
-		});
-		if(typeof(command) == "function")
-			return command;
-		else
-		{
-			if(typeof Logger != "undefined")
-				Logger.error("Missing function", this);
-				
-			throw new Error	("Missing function '"+this+"'");
-		}
-	},
-
-	/** 
-	 * Convert a string into integer, returns null if not integer.
-	 * @return {Number} null if string does not represent an integer.
-	 */
-	toInteger : function()
-	{
-		var exp = /^\s*[-\+]?\d+\s*$/;
-		if (this.match(exp) == null)
-			return null;
-		var num = parseInt(this, 10);
-		return (isNaN(num) ? null : num);
-	},
-
-	/** 
-	 * Convert a string into a double/float value. <b>Internationalization 
-	 * is not supported</b>
-	 * @param {String} the decimal character
-	 * @return {Double} null if string does not represent a float value
-	 */
-	toDouble : function(decimalchar)
-	{
-		if(this.length <= 0) return null;
-		decimalchar = decimalchar || ".";
-		var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$");
-		var m = this.match(exp);
-		
-		if (m == null)	
-			return null;
-		m[1] = m[1] || "";
-		m[2] = m[2] || "0";
-		m[4] = m[4] || "0";
-				
-		var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
-		var num = parseFloat(cleanInput);
-		return (isNaN(num) ? null : num);
-	},
-
-	/**
-	 * Convert strings that represent a currency value (e.g. a float with grouping 
-	 * characters) to float. E.g. "10,000.50" will become "10000.50". The number 
-	 * of dicimal digits, grouping and decimal characters can be specified.
-	 * <i>The currency input format is <b>very</b> strict, null will be returned if
-	 * the pattern does not match</i>.
-	 * @param {String} the grouping character, default is ","
-	 * @param {Number} number of decimal digits
-	 * @param {String} the decimal character, default is "."
-	 * @type {Double} the currency value as float.
-	 */
-	toCurrency : function(groupchar, digits, decimalchar)
-	{
-		groupchar = groupchar || ",";
-		decimalchar = decimalchar || ".";
-		digits = typeof(digits) == "undefined" ? 2 : digits;
-
-		var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)"
-			+ ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "")
-			+ "\\s*$");
-		var m = this.match(exp);
-		if (m == null)
-			return null;
-		var intermed = m[2] + m[5] ;
-		var cleanInput = m[1] + intermed.replace(
-				new RegExp("(\\" + groupchar + ")", "g"), "") 
-								+ ((digits > 0) ? "." + m[7] : "");
-		var num = parseFloat(cleanInput);
-		return (isNaN(num) ? null : num);
-	},
-
-	/**
-	 * Converts the string to a date by finding values that matches the 
-	 * date format pattern.
-	 * @param string date format pattern, e.g. MM-dd-yyyy
-	 * @return {Date} the date extracted from the string
-	 */
-	toDate : function(format)
-	{
-		return Date.SimpleParse(this, format);
-	}
-});
-
-var $break    = new Object();
-var $continue = new Object();
+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;
-        }
+        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) {
@@ -797,25 +449,25 @@ var Enumerable = {
     });
     return result;
   },
-  
+
   any: function(iterator) {
-    var result = true;
+    var result = false;
     this.each(function(value, index) {
-      if (result = !!(iterator || Prototype.K)(value, index)) 
+      if (result = !!(iterator || Prototype.K)(value, index))
         throw $break;
     });
     return result;
   },
-  
+
   collect: function(iterator) {
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push((iterator || Prototype.K)(value, index));
     });
     return results;
   },
-  
-  detect: function (iterator) {
+
+  detect: function(iterator) {
     var result;
     this.each(function(value, index) {
       if (iterator(value, index)) {
@@ -825,7 +477,7 @@ var Enumerable = {
     });
     return result;
   },
-  
+
   findAll: function(iterator) {
     var results = [];
     this.each(function(value, index) {
@@ -834,7 +486,7 @@ var Enumerable = {
     });
     return results;
   },
-  
+
   grep: function(pattern, iterator) {
     var results = [];
     this.each(function(value, index) {
@@ -844,7 +496,7 @@ var Enumerable = {
     })
     return results;
   },
-  
+
   include: function(object) {
     var found = false;
     this.each(function(value) {
@@ -855,21 +507,29 @@ var Enumerable = {
     });
     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.collect(function(value) {
+    return this.map(function(value) {
       return value[method].apply(value, args);
     });
   },
-  
+
   max: function(iterator) {
     var result;
     this.each(function(value, index) {
@@ -879,7 +539,7 @@ var Enumerable = {
     });
     return result;
   },
-  
+
   min: function(iterator) {
     var result;
     this.each(function(value, index) {
@@ -889,16 +549,16 @@ var Enumerable = {
     });
     return result;
   },
-  
+
   partition: function(iterator) {
     var trues = [], falses = [];
     this.each(function(value, index) {
-      ((iterator || Prototype.K)(value, index) ? 
+      ((iterator || Prototype.K)(value, index) ?
         trues : falses).push(value);
     });
     return [trues, falses];
   },
-  
+
   pluck: function(property) {
     var results = [];
     this.each(function(value, index) {
@@ -906,7 +566,7 @@ var Enumerable = {
     });
     return results;
   },
-  
+
   reject: function(iterator) {
     var results = [];
     this.each(function(value, index) {
@@ -915,20 +575,20 @@ var Enumerable = {
     });
     return results;
   },
-  
+
   sortBy: function(iterator) {
-    return this.collect(function(value, index) {
+    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.collect(Prototype.K);
+    return this.map();
   },
-  
+
   zip: function() {
     var iterator = Prototype.K, args = $A(arguments);
     if (typeof args.last() == 'function')
@@ -939,7 +599,11 @@ var Enumerable = {
       return iterator(collections.pluck(index));
     });
   },
-  
+
+  size: function() {
+    return this.toArray().length;
+  },
+
   inspect: function() {
     return '#<Enumerable:' + this.toArray().inspect() + '>';
   }
@@ -952,20 +616,33 @@ Object.extend(Enumerable, {
   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++)
+    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)
@@ -973,64 +650,157 @@ if (!Array.prototype._reverse)
 
 Object.extend(Array.prototype, {
   _each: function(iterator) {
-    for (var i = 0; i < this.length; i++)
+    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 != undefined || value != null;
+      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; i < this.length; i++)
+    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)));
+}
 
-var Hash = {
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
   _each: function(iterator) {
     for (var key in this) {
       var value = this[key];
-      if (typeof value == 'function') continue;
+      if (value && value == Hash.prototype[key]) continue;
 
       var pair = [key, value];
       pair.key = key;
@@ -1048,43 +818,66 @@ var Hash = {
   },
 
   merge: function(hash) {
-    return $H(hash).inject($H(this), function(mergedHash, pair) {
+    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 this.map(function(pair)
-	{
-	  //special case for PHP, array post data.
-	  if(typeof(pair[1]) == 'object' || typeof(pair[1]) == 'array')
-	  {
-	  	return $A(pair[1]).collect(function(value)
-		{
-			return encodeURIComponent(pair[0])+'='+encodeURIComponent(value);
-		}).join('&');
-	  }
-	  else
- 	     return pair.map(encodeURIComponent).join('=');
-    }).join('&');
+    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) {
-  var hash = Object.extend({}, object || {});
-  Object.extend(hash, Enumerable);
-  Object.extend(hash, Hash);
-  return hash;
-}
-
-
+  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, {
@@ -1093,17 +886,17 @@ Object.extend(ObjectRange.prototype, {
     this.end = end;
     this.exclusive = exclusive;
   },
-  
+
   _each: function(iterator) {
     var value = this.start;
-    do {
+    while (this.include(value)) {
       iterator(value);
       value = value.succ();
-    } while (this.include(value));
+    }
   },
-  
+
   include: function(value) {
-    if (value < this.start) 
+    if (value < this.start)
       return false;
     if (this.exclusive)
       return value < this.end;
@@ -1115,94 +908,435 @@ var $R = function(start, end, exclusive) {
   return new ObjectRange(start, end, exclusive);
 }
 
-function $() {
-  var results = [], element;
-  for (var i = 0; i < arguments.length; i++) {
-    element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-    results.push(Element.extend(element));
-  }
-  return results.length < 2 ? results[0] : results;
-}
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
 
-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(Element.extend(child));
-    return elements;
-  });
+  activeRequestCount: 0
 }
 
-/*--------------------------------------------------------------------------*/
+Ajax.Responders = {
+  responders: [],
 
-if (!window.Element)
-  var Element = new Object();
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
 
-Element.extend = function(element) {
-  if (!element) return;
-  if (_nativeExtensions) return element;
-  
-  if (!element._extended && element.tagName && element != window) {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
-      var value = methods[property];
-      if (typeof value == 'function')
-        element[property] = cache.findOrStore(value);
-    }
-  }
-  
-  element._extended = true;
-  return element;
+  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() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      Element[Element.visible(element) ? 'hide' : 'show'](element);
-    }
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
   },
 
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
-    }
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
   },
-  
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
-    }
+
+  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 {
@@ -1212,105 +1346,250 @@ Element.Methods = {
         range.createContextualFragment(html.stripScripts()), element);
     }
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
-  
-  getHeight: function(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);
-    return element.offsetHeight; 
+    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;
-    return Element.classNames(element).include(className);
+    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;
-    return Element.classNames(element).add(className);
+    Element.classNames(element).add(className);
+    return element;
   },
 
   removeClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).remove(className);
+    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);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
-      if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 
-        Element.remove(node);
+    var 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.match(/^\s*$/);
+    return $(element).innerHTML.blank();
   },
-  
-  childOf: function(element, ancestor) {
+
+  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 x = element.x ? element.x : element.offsetLeft,
-        y = element.y ? element.y : element.offsetTop;
-    window.scrollTo(x, y);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
   },
-  
+
   getStyle: function(element, style) {
     element = $(element);
-    var value = element.style[style.camelize()];
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
     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()];
-      }
+      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;
+  },
 
-    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
-      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
 
-    return value == 'auto' ? null : value;
+  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;
   },
-  
-  setStyle: function(element, style) {
+
+  setOpacity: function(element, value) {
     element = $(element);
-    for (var name in style) 
-      element.style[name.camelize()] = style[name];
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
   },
-  
+
   getDimensions: function(element) {
     element = $(element);
-    if (Element.getStyle(element, 'display') != 'none')
+    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 = '';
+    els.display = 'block';
     var originalWidth = element.clientWidth;
     var originalHeight = element.clientHeight;
-    els.display = 'none';
+    els.display = originalDisplay;
     els.position = originalPosition;
     els.visibility = originalVisibility;
-    return {width: originalWidth, height: originalHeight};    
+    return {width: originalWidth, height: originalHeight};
   },
-  
+
   makePositioned: function(element) {
     element = $(element);
     var pos = Element.getStyle(element, 'position');
@@ -1322,8 +1601,9 @@ Element.Methods = {
       if (window.opera) {
         element.style.top = 0;
         element.style.left = 0;
-      }  
+      }
     }
+    return element;
   },
 
   undoPositioned: function(element) {
@@ -1334,53 +1614,253 @@ Element.Methods = {
         element.style.top =
         element.style.left =
         element.style.bottom =
-        element.style.right = '';   
+        element.style.right = '';
     }
+    return element;
   },
 
   makeClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element._overflow = element.style.overflow;
+    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.style.overflow = element._overflow;
-    element._overflow = undefined;
+    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;
+    }
 
-Object.extend(Element, Element.Methods);
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
 
-var _nativeExtensions = false;
+  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;
+  };
 
-if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
-  var HTMLElement = {}
-  HTMLElement.prototype = document.createElement('div').__proto__;
-}
+  // 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) {
-  Object.extend(Element.Methods, methods || {});
-  
-  if(typeof HTMLElement != 'undefined') {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in 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 (typeof value == 'function')
-        HTMLElement.prototype[property] = cache.findOrStore(value);
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
     }
-    _nativeExtensions = true;
   }
-}
 
-Element.addMethods();
+  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 = new Object();
-Toggle.display = Element.toggle;
+var Toggle = { display: Element.toggle };
 
 /*--------------------------------------------------------------------------*/
 
@@ -1392,13 +1872,13 @@ 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.toLowerCase();
-        if (tagName == 'tbody' || tagName == 'tr') {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
           this.insertContent(this.contentFromAnonymousTable());
         } else {
           throw e;
@@ -1410,9 +1890,9 @@ Abstract.Insertion.prototype = {
       this.insertContent([this.range.createContextualFragment(this.content)]);
     }
 
-    setTimeout(function() {content.evalScripts()}, 10);   
+    setTimeout(function() {content.evalScripts()}, 10);
   },
-  
+
   contentFromAnonymousTable: function() {
     var div = document.createElement('div');
     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
@@ -1427,7 +1907,7 @@ 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);
@@ -1441,7 +1921,7 @@ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
     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);
@@ -1455,7 +1935,7 @@ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'),
     this.range.selectNodeContents(this.element);
     this.range.collapse(this.element);
   },
-  
+
   insertContent: function(fragments) {
     fragments.each((function(fragment) {
       this.element.appendChild(fragment);
@@ -1468,10 +1948,10 @@ 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.parentNode.insertBefore(fragment,
         this.element.nextSibling);
     }).bind(this));
   }
@@ -1490,225 +1970,818 @@ Element.ClassNames.prototype = {
       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(' '));
+    this.set($A(this).concat(classNameToAdd).join(' '));
   },
-  
+
   remove: function(classNameToRemove) {
     if (!this.include(classNameToRemove)) return;
-    this.set(this.select(function(className) {
-      return className != classNameToRemove;
-    }).join(' '));
+    this.set($A(this).without(classNameToRemove).join(' '));
   },
-  
+
   toString: function() {
-    return this.toArray().join(' ');
+    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();
 
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $(arguments[i]).value = '';
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
   },
 
-  focus: function(element) {
-    $(element).focus();
+  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;
   },
 
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
-    return true;
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
   },
 
-  select: function(element) {
-    $(element).select();
+  match: function(element) {
+    return this.findElements(document).include(element);
   },
 
-  activate: function(element) {
-    element = $(element);
-    element.focus();
-    if (element.select)
-      element.select();
+  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:         new RegExp(/\[((?:[\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;
+    },
 
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (combinator) nodes = this[combinator](nodes);
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
 
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
+  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;
     }
+  },
 
-    return queryComponents.join('&');
+  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() + '-'); }
   },
 
-  getElements: function(form) {
-    form = $(form);
-    var elements = new Array();
+  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;
+  },
 
-    for (var tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
     }
-    return elements;
+    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 inputs;
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
 
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
       var input = inputs[i];
-      if ((typeName && input.type != typeName) ||
-          (name && input.name != name))
+      if ((typeName && input.type != typeName) || (name && input.name != name))
         continue;
-      matchingInputs.push(input);
+      matchingInputs.push(Element.extend(input));
     }
 
     return matchingInputs;
   },
 
   disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.blur();
       element.disabled = 'true';
-    }
+    });
+    return form;
   },
 
   enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.disabled = '';
-    }
+    });
+    return form;
   },
 
   findFirstElement: function(form) {
-    return Form.getElements(form).find(function(element) {
+    return $(form).getElements().find(function(element) {
       return element.type != 'hidden' && !element.disabled &&
         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     });
   },
 
   focusFirstElement: function(form) {
-    Field.activate(Form.findFirstElement(form));
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
   },
 
-  reset: function(form) {
-    $(form).reset();
+  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();
-    var parameter = Form.Element.Serializers[method](element);
+    return Form.Element.Serializers[method](element);
+  },
 
-    if (parameter) {
-      var key = encodeURIComponent(parameter[0]);
-      if (key.length == 0) return;
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
 
-      if (parameter[1].constructor != Array)
-        parameter[1] = [parameter[1]];
+  present: function(element) {
+    return $(element).value != '';
+  },
 
-      return parameter[1].map(function(value) {
-        return key + '=' + encodeURIComponent(value);
-      }).join('&');
-    }
+  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;
   },
 
-  getValue: function(element) {
+  disable: function(element) {
     element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
 
-    if (parameter)
-      return parameter[1];
+  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) {
-  	if(typeof(element.type) == "undefined")
-		return false;
     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);
+      default:
+        return Form.Element.Serializers.textarea(element);
     }
-    return false;
   },
 
   inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
+    return element.checked ? element.value : null;
   },
 
   textarea: function(element) {
-    return [element.name, element.value];
+    return element.value;
   },
 
   select: function(element) {
-    return Form.Element.Serializers[element.type == 'select-one' ?
+    return this[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 || opt.text;
-    }
-    return [element.name, value];
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
   },
 
   selectMany: function(element) {
-    var value = [];
-    for (var i = 0; i < element.length; i++) {
+    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)
-        value.push(opt.value || opt.text);
+      if (opt.selected) values.push(this.optionValue(opt));
     }
-    return [element.name, value];
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
   }
 }
 
 /*--------------------------------------------------------------------------*/
 
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
 Abstract.TimedObserver = function() {}
 Abstract.TimedObserver.prototype = {
   initialize: function(element, frequency, callback) {
@@ -1726,7 +2799,9 @@ Abstract.TimedObserver.prototype = {
 
   onTimerEvent: function() {
     var value = this.getValue();
-    if (this.lastValue != value) {
+    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;
     }
@@ -1771,9 +2846,7 @@ Abstract.EventObserver.prototype = {
   },
 
   registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
   },
 
   registerCallback: function(element) {
@@ -1783,11 +2856,7 @@ Abstract.EventObserver.prototype = {
         case 'radio':
           Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
+        default:
           Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
@@ -1808,9 +2877,6 @@ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
     return Form.serialize(this.element);
   }
 });
-
-
-
 if (!window.Event) {
   var Event = new Object();
 }
@@ -1825,7 +2891,10 @@ Object.extend(Event, {
   KEY_RIGHT:    39,
   KEY_DOWN:     40,
   KEY_DELETE:   46,
-  KEY_SPACEBAR: 32,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
 
   element: function(event) {
     return event.target || event.srcElement;
@@ -1837,19 +2906,19 @@ Object.extend(Event, {
   },
 
   pointerX: function(event) {
-    return event.pageX || (event.clientX + 
+    return event.pageX || (event.clientX +
       (document.documentElement.scrollLeft || document.body.scrollLeft));
   },
 
   pointerY: function(event) {
-    return event.pageY || (event.clientY + 
+    return event.pageY || (event.clientY +
       (document.documentElement.scrollTop || document.body.scrollTop));
   },
 
   stop: function(event) {
-    if (event.preventDefault) { 
-      event.preventDefault(); 
-      event.stopPropagation(); 
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
     } else {
       event.returnValue = false;
       event.cancelBubble = true;
@@ -1867,7 +2936,7 @@ Object.extend(Event, {
   },
 
   observers: false,
-  
+
   _observeAndCache: function(element, name, observer, useCapture) {
     if (!this.observers) this.observers = [];
     if (element.addEventListener) {
@@ -1878,10 +2947,10 @@ Object.extend(Event, {
       element.attachEvent('on' + name, observer);
     }
   },
-  
+
   unloadCache: function() {
     if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
       Event.stopObserving.apply(this, Event.observers[i]);
       Event.observers[i][0] = null;
     }
@@ -1889,174 +2958,61 @@ Object.extend(Event, {
   },
 
   observe: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
-    
+
     if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.attachEvent))
+      (Prototype.Browser.WebKit || element.attachEvent))
       name = 'keydown';
-    
-    this._observeAndCache(element, name, observer, useCapture);
+
+    Event._observeAndCache(element, name, observer, useCapture);
   },
 
   stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
-    
+
     if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.detachEvent))
+        (Prototype.Browser.WebKit || element.attachEvent))
       name = 'keydown';
-    
+
     if (element.removeEventListener) {
       element.removeEventListener(name, observer, useCapture);
     } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
     }
   }
 });
 
 /* prevent memory leaks in IE */
-if (navigator.appVersion.match(/\bMSIE\b/))
+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,
 
-
-/**
- * @class Event extensions.
- */
-Object.extend(Event,
-{
-	/**
-	 * Register a function to be executed when the page is loaded.
-	 * Note that the page is only loaded if all resources (e.g. images)
-	 * are loaded.
-	 *
-	 * Example: Show an alert box with message "Page Loaded!" when the
-	 * page finished loading.
-	 * <code>
-	 * Event.OnLoad(function(){ alert("Page Loaded!"); });
-	 * </code>
-	 *
-	 * @param {Function} function to execute when page is loaded.
-	 */
-	OnLoad : function (fn)
-	{
-		// opera onload is in document, not window
-		var w = document.addEventListener &&
-					!window.addEventListener ? document : window;
-		Event.observe(w,'load',fn);
-	},
-
-	/**
-	 * @param {Event} a keyboard event
-	 * @return {Number} the Unicode character code generated by the key
-	 * that was struck.
-	 */
-	keyCode : function(e)
-	{
-	   return e.keyCode != null ? e.keyCode : e.charCode
-	},
-
-	/**
-	 * @param {String} event type or event name.
-	 * @return {Boolean} true if event type is of HTMLEvent, false
-	 * otherwise
-	 */
-	isHTMLEvent : function(type)
-	{
-		var events = ['abort', 'blur', 'change', 'error', 'focus',
-					'load', 'reset', 'resize', 'scroll', 'select',
-					'submit', 'unload'];
-		return events.include(type);
-	},
-
-	/**
-	 * @param {String} event type or event name
-	 * @return {Boolean} true if event type is of MouseEvent,
-	 * false otherwise
-	 */
-	isMouseEvent : function(type)
-	{
-		var events = ['click', 'mousedown', 'mousemove', 'mouseout',
-					'mouseover', 'mouseup'];
-		return events.include(type);
-	},
-
-	/**
-	 * Dispatch the DOM event of a given <tt>type</tt> on a DOM
-	 * <tt>element</tt>. Only HTMLEvent and MouseEvent can be
-	 * dispatched, keyboard events or UIEvent can not be dispatch
-	 * via javascript consistently.
-	 * For the "submit" event the submit() method is called.
-	 * @param {Object} element id string or a DOM element.
-	 * @param {String} event type to dispatch.
-	 */
-	fireEvent : function(element,type,canBubble)
-	{
-		canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
-		element = $(element);
-		if(type == "submit")
-			return element.submit();
-		if(document.createEvent)
-        {
-			if(Event.isHTMLEvent(type))
-			{
-				var event = document.createEvent('HTMLEvents');
-	            event.initEvent(type, canBubble, true);
-			}
-			else if(Event.isMouseEvent(type))
-			{
-				var event = document.createEvent('MouseEvents');
-				if (event.initMouseEvent)
-		        {
-					event.initMouseEvent(type,canBubble,true,
-						document.defaultView, 1, 0, 0, 0, 0, false,
-								false, false, false, 0, null);
-		        }
-		        else
-		        {
-		            // Safari
-		            // TODO we should be initialising other mouse-event related attributes here
-		            event.initEvent(type, canBubble, true);
-		        }
-			}
-            element.dispatchEvent(event);
-        }
-        else if(document.createEventObject)
-        {
-        	var evObj = document.createEventObject();
-            element.fireEvent('on'+type, evObj);
-        }
-        else if(typeof(element['on'+type]) == "function")
-            element['on'+type]();
-	}
-});
-
-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;
-  },
+  // 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; 
+      valueL += element.scrollLeft || 0;
       element = element.parentNode;
     } while (element);
     return [valueL, valueT];
@@ -2079,13 +3035,14 @@ var Position = {
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        p = Element.getStyle(element, 'position');
+        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;
@@ -2096,7 +3053,7 @@ var Position = {
 
     return document.body;
   },
-  
+
   // caches x/y coordinate pair to use with overlap
   within: function(element, x, y) {
     if (this.includeScrollOffsets)
@@ -2107,7 +3064,7 @@ var Position = {
 
     return (y >= this.offset[1] &&
             y <  this.offset[1] + element.offsetHeight &&
-            x >= this.offset[0] && 
+            x >= this.offset[0] &&
             x <  this.offset[0] + element.offsetWidth);
   },
 
@@ -2120,32 +3077,21 @@ var Position = {
 
     return (this.ycomp >= this.offset[1] &&
             this.ycomp <  this.offset[1] + element.offsetHeight &&
-            this.xcomp >= this.offset[0] && 
+            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) / 
+  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) / 
+      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;
 
@@ -2155,15 +3101,17 @@ var Position = {
       valueL += element.offsetLeft || 0;
 
       // Safari fix
-      if (element.offsetParent==document.body)
+      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;    
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
     } while (element = element.parentNode);
 
     return [valueL, valueT];
@@ -2187,7 +3135,7 @@ var Position = {
     target = $(target);
     var delta = [0, 0];
     var parent = null;
-    // delta [0,0] will do fine with position: fixed elements, 
+    // 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);
@@ -2197,7 +3145,7 @@ var Position = {
     // correct by body offsets (fixes Safari)
     if (parent == document.body) {
       delta[0] -= document.body.offsetLeft;
-      delta[1] -= document.body.offsetTop; 
+      delta[1] -= document.body.offsetTop;
     }
 
     // set position
@@ -2224,10 +3172,10 @@ var Position = {
     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';;
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
   },
 
   relativize: function(element) {
@@ -2249,7 +3197,7 @@ var Position = {
 // Safari returns margins on body which is incorrect if the child is absolutely
 // positioned.  For performance reasons, redefine Position.cumulativeOffset for
 // KHTML/WebKit only.
-if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+if (Prototype.Browser.WebKit) {
   Position.cumulativeOffset = function(element) {
     var valueT = 0, valueL = 0;
     do {
@@ -2257,132 +3205,22 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
       valueL += element.offsetLeft || 0;
       if (element.offsetParent == document.body)
         if (Element.getStyle(element, 'position') == 'absolute') break;
-        
+
       element = element.offsetParent;
     } while (element);
-    
-    return [valueL, valueT];
-  }
-}
-
-
-
-
-var Selector = Class.create();
-Selector.prototype = {
-  initialize: function(expression) {
-    this.params = {classNames: []};
-    this.expression = expression.toString().strip();
-    this.parseExpression();
-    this.compileMatcher();
-  },
-
-  parseExpression: function() {
-    function abort(message) { throw 'Parse error in selector: ' + message; }
-
-    if (this.expression == '')  abort('empty expression');
-
-    var params = this.params, expr = this.expression, match, modifier, clause, rest;
-    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
-      params.attributes = params.attributes || [];
-      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
-      expr = match[1];
-    }
-
-    if (expr == '*') return this.params.wildcard = true;
-    
-    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
-      modifier = match[1], clause = match[2], rest = match[3];
-      switch (modifier) {
-        case '#':       params.id = clause; break;
-        case '.':       params.classNames.push(clause); break;
-        case '':
-        case undefined: params.tagName = clause.toUpperCase(); break;
-        default:        abort(expr.inspect());
-      }
-      expr = rest;
-    }
-    
-    if (expr.length > 0) abort(expr.inspect());
-  },
-
-  buildMatchExpression: function() {
-    var params = this.params, conditions = [], clause;
-
-    if (params.wildcard)
-      conditions.push('true');
-    if (clause = params.id)
-      conditions.push('element.id == ' + clause.inspect());
-    if (clause = params.tagName)
-      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
-    if ((clause = params.classNames).length > 0)
-      for (var i = 0; i < clause.length; i++)
-        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
-    if (clause = params.attributes) {
-      clause.each(function(attribute) {
-        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
-        var splitValueBy = function(delimiter) {
-          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
-        }
-        
-        switch (attribute.operator) {
-          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
-          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
-          case '|=':      conditions.push(
-                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
-                          ); break;
-          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
-          case '':
-          case undefined: conditions.push(value + ' != null'); break;
-          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
-        }
-      });
-    }
-
-    return conditions.join(' && ');
-  },
-
-  compileMatcher: function() {
-    this.match = new Function('element', 'if (!element.tagName) return false; \
-      return ' + this.buildMatchExpression());
-  },
-
-  findElements: function(scope) {
-    var element;
-
-    if (element = $(this.params.id))
-      if (this.match(element))
-        if (!scope || Element.childOf(element, scope))
-          return [element];
-
-    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
-
-    var results = [];
-    for (var i = 0; i < scope.length; i++)
-      if (this.match(element = scope[i]))
-        results.push(Element.extend(element));
-
-    return results;
-  },
 
-  toString: function() {
-    return this.expression;
+    return [valueL, valueT];
   }
 }
 
-function $$() {
-  return $A(arguments).map(function(expression) {
-    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
-      var selector = new Selector(expr);
-      return results.map(selector.findElements.bind(selector)).flatten();
-    });
-  }).flatten();
-}
+Element.addMethods();
 
+// script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
 
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
 //
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
 var Builder = {
   NODEMAP: {
@@ -2415,7 +3253,7 @@ var Builder = {
     var element = parentElement.firstChild || null;
       
     // see if browser added wrapping tags
-    if(element && (element.tagName != elementName))
+    if(element && (element.tagName.toUpperCase() != elementName))
       element = element.getElementsByTagName(elementName)[0];
     
     // fallback to createElement approach
@@ -2443,7 +3281,7 @@ var Builder = {
               for(attr in arguments[1]) 
                 element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
             }
-            if(element.tagName != elementName)
+            if(element.tagName.toUpperCase() != elementName)
               element = parentElement.getElementsByTagName(elementName)[0];
             }
         } 
@@ -2457,10 +3295,16 @@ var Builder = {
   _text: function(text) {
      return document.createTextNode(text);
   },
+
+  ATTR_MAP: {
+    'className': 'class',
+    'htmlFor': 'for'
+  },
+
   _attributes: function(attributes) {
     var attrs = [];
     for(attribute in attributes)
-      attrs.push((attribute=='className' ? 'class' : attribute) +
+      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
           '="' + attributes[attribute].toString().escapeHTML() + '"');
     return attrs.join(" ");
   },
@@ -2479,34 +3323,648 @@ var Builder = {
   },
   _isStringOrNumber: function(param) {
     return(typeof param=='string' || typeof param=='number');
+  },
+  build: function(html) {
+    var element = this.node('div');
+    $(element).update(html.strip());
+    return element.down();
+  },
+  dump: function(scope) { 
+    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
+  
+    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+  
+    tags.each( function(tag){ 
+      scope[tag] = function() { 
+        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
+      } 
+    });
   }
 }
 
+
+
+/**
+ * Similar to bindAsEventLister, but takes additional arguments.
+ */
+Function.prototype.bindEvent = function()
+{
+	var __method = this, args = $A(arguments), object = args.shift();
+	return function(event)
+	{
+		return __method.apply(object, [event || window.event].concat(args));
+	}
+}
+
+/**
+ * Creates a new function by copying function definition from
+ * the <tt>base</tt> and optional <tt>definition</tt>.
+ * @param function a base function to copy from.
+ * @param array additional definition
+ * @param function return a new function with definition from both
+ * <tt>base</tt> and <tt>definition</tt>.
+ */
+Class.extend = function(base, definition)
+{
+		var component = Class.create();
+		Object.extend(component.prototype, base.prototype);
+		if(definition)
+			Object.extend(component.prototype, definition);
+		return component;
+}
+
+/*
+	Base, version 1.0.2
+	Copyright 2006, Dean Edwards
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+var Base = function() {
+	if (arguments.length) {
+		if (this == window) { // cast an object to this class
+			Base.prototype.extend.call(arguments[0], arguments.callee.prototype);
+		} else {
+			this.extend(arguments[0]);
+		}
+	}
+};
+
+Base.version = "1.0.2";
+
+Base.prototype = {
+	extend: function(source, value) {
+		var extend = Base.prototype.extend;
+		if (arguments.length == 2) {
+			var ancestor = this[source];
+			// overriding?
+			if ((ancestor instanceof Function) && (value instanceof Function) &&
+				ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
+				var method = value;
+			//	var _prototype = this.constructor.prototype;
+			//	var fromPrototype = !Base._prototyping && _prototype[source] == ancestor;
+				value = function() {
+					var previous = this.base;
+				//	this.base = fromPrototype ? _prototype[source] : ancestor;
+					this.base = ancestor;
+					var returnValue = method.apply(this, arguments);
+					this.base = previous;
+					return returnValue;
+				};
+				// point to the underlying method
+				value.valueOf = function() {
+					return method;
+				};
+				value.toString = function() {
+					return String(method);
+				};
+			}
+			return this[source] = value;
+		} else if (source) {
+			var _prototype = {toSource: null};
+			// do the "toString" and other methods manually
+			var _protected = ["toString", "valueOf"];
+			// if we are prototyping then include the constructor
+			if (Base._prototyping) _protected[2] = "constructor";
+			for (var i = 0; (name = _protected[i]); i++) {
+				if (source[name] != _prototype[name]) {
+					extend.call(this, name, source[name]);
+				}
+			}
+			// copy each of the source object's properties to this object
+			for (var name in source) {
+				if (!_prototype[name]) {
+					extend.call(this, name, source[name]);
+				}
+			}
+		}
+		return this;
+	},
+
+	base: function() {
+		// call this method from any other method to invoke that method's ancestor
+	}
+};
+
+Base.extend = function(_instance, _static) {
+	var extend = Base.prototype.extend;
+	if (!_instance) _instance = {};
+	// build the prototype
+	Base._prototyping = true;
+	var _prototype = new this;
+	extend.call(_prototype, _instance);
+	var constructor = _prototype.constructor;
+	_prototype.constructor = this;
+	delete Base._prototyping;
+	// create the wrapper for the constructor function
+	var klass = function() {
+		if (!Base._prototyping) constructor.apply(this, arguments);
+		this.constructor = klass;
+	};
+	klass.prototype = _prototype;
+	// build the class interface
+	klass.extend = this.extend;
+	klass.implement = this.implement;
+	klass.toString = function() {
+		return String(constructor);
+	};
+	extend.call(klass, _static);
+	// single instance
+	var object = constructor ? klass : _prototype;
+	// class initialisation
+	if (object.init instanceof Function) object.init();
+	return object;
+};
+
+Base.implement = function(_interface) {
+	if (_interface instanceof Function) _interface = _interface.prototype;
+	this.prototype.extend(_interface);
+};
+
+/*
+ * Signals and Slots for Prototype: Easy custom javascript events
+ * http://tetlaw.id.au/view/blog/signals-and-slots-for-prototype-easy-custom-javascript-events
+ * Andrew Tetlaw
+ * Version 1.2 (2006-06-19)
+ *
+ * http://creativecommons.org/licenses/by-sa/2.5/
+ *
+Signal = {
+	throwErrors : true,
+	MT : function(){ return true },
+	connect : function(obj1, func1, obj2, func2, options) {
+		var options = Object.extend({
+			connectOnce : false,
+			before : false,
+			mutate : function() {return arguments;}
+		}, options || {});
+		if(typeof func1 != 'string' || typeof func2 != 'string') return;
+
+		var sigObj = obj1 || window;
+		var slotObj = obj2 || window;
+		var signame = func1+'__signal_';
+		var slotsname = func1+'__slots_';
+		if(!sigObj[signame]) {
+			// having the slotFunc in a var and setting it by using an anonymous function in this way
+			// is apparently a good way to prevent memory leaks in IE if the objects are DOM nodes.
+			var slotFunc = function() {
+				var args = [];
+				for(var x = 0; x < arguments.length; x++){
+					args.push(arguments[x]);
+				}
+				args = options.mutate.apply(null,args)
+				var result;
+				if(!options.before) result = sigObj[signame].apply(sigObj,arguments); //default: call sign before slot
+				sigObj[slotsname].each(function(slot){
+					try {
+						if(slot && slot[0]) { // testing for null, a disconnect may have nulled this slot
+							slot[0][slot[1]].apply(slot[0],args); //[0] = obj, [1] = func name
+						}
+					} catch(e) {
+						if(Signal.throwErrors) throw e;
+					}
+				});
+				if(options.before) result = sigObj[signame].apply(sigObj,arguments); //call slot before sig
+				return result; //return sig result
+			};
+			(function() {
+				sigObj[slotsname] = $A([]);
+				sigObj[signame] = sigObj[func1] || Signal.MT;
+				sigObj[func1] = slotFunc;
+			})();
+		}
+		var con = (sigObj[slotsname].length > 0) ?
+					(options.connectOnce ? !sigObj[slotsname].any(function(slot) { return (slot[0] == slotObj && slot[1] == func2) }) : true) :
+					true;
+		if(con) {
+			sigObj[slotsname].push([slotObj,func2]);
+		}
+	},
+	connectOnce : function(obj1, func1, obj2, func2, options) {
+		Signal.connect(obj1, func1, obj2, func2, Object.extend(options || {}, {connectOnce : true}))
+	},
+	disconnect : function(obj1, func1, obj2, func2, options) {
+		var options = Object.extend({
+			disconnectAll : false
+		}, options || {});
+		if(typeof func1 != 'string' || typeof func2 != 'string') return;
+
+		var sigObj = obj1 || window;
+		var slotObj = obj2 || window;
+		var signame = func1+'__signal_';
+		var slotsname = func1+'__slots_';
+
+		// I null them in this way so that any currectly active signal will read a null slot,
+		// otherwise the slot will be applied even though it's been disconnected
+		if(sigObj[slotsname]) {
+			if(options.disconnectAll) {
+				sigObj[slotsname] = sigObj[slotsname].collect(function(slot) {
+					if(slot[0] == slotObj && slot[1] == func2) {
+						slot[0] = null;
+						return null;
+					} else {
+						return slot;
+					}
+				}).compact();
+			} else {
+				var idx = -1;
+				sigObj[slotsname] = sigObj[slotsname].collect(function(slot, index) {
+					if(slot[0] == slotObj && slot[1] == func2 && idx < 0) {  //disconnect first match
+						idx = index;
+						slot[0] = null;
+						return null;
+					} else {
+						return slot;
+					}
+				}).compact();
+			}
+		}
+	},
+	disconnectAll : function(obj1, func1, obj2, func2, options) {
+		Signal.disconnect(obj1, func1, obj2, func2, Object.extend(options || {}, {disconnectAll : true}))
+	}
+}
+*/
+
+/*
+ Tests
+
+//   1. Simple Test 1 "hello Fred" should trigger "Fred is a stupid head"
+
+
+      sayHello = function(n) {
+      	alert("Hello! " + n);
+      }
+      moron = function(n) {
+      	alert(n + " is a stupid head");
+      }
+      Signal.connect(null,'sayHello',null,'moron');
+
+      onclick="sayHello('Fred')"
+
+
+//   2. Simple Test 2 repeated insults about Fred
+
+
+      Signal.connect(null,'sayHello2',null,'moron2');
+      Signal.connect(null,'sayHello2',null,'moron2');
+      Signal.connect(null,'sayHello2',null,'moron2');
+
+
+//   3. Simple Test 3 multiple insults about Fred
+
+
+      Signal.connect(null,'sayHello3',null,'moron3');
+      Signal.connect(null,'sayHello3',null,'bonehead3');
+      Signal.connect(null,'sayHello3',null,'idiot3');
+
+
+//   4. Simple Test 4 3 insults about Fred first - 3 then none
+
+
+      Signal.connect(null,'sayHello4',null,'moron4');
+      Signal.connect(null,'sayHello4',null,'moron4');
+      Signal.connect(null,'sayHello4',null,'moron4');
+      Signal.disconnect(null,'sayHello4',null,'moron4');
+      Signal.disconnect(null,'sayHello4',null,'moron4');
+      Signal.disconnect(null,'sayHello4',null,'moron4');
+
+
+//   5. Simple Test 5 connect 3 insults about Fred first - only one, then none
+
+
+      Signal.connect(null,'sayHello5',null,'moron5');
+      Signal.connect(null,'sayHello5',null,'moron5');
+      Signal.connect(null,'sayHello5',null,'moron5');
+      Signal.disconnectAll(null,'sayHello5',null,'moron5');
+
+
+//   6. Simple Test 6 connect 3 insults but only one comes out
+
+
+      Signal.connectOnce(null,'sayHello6',null,'moron6');
+      Signal.connectOnce(null,'sayHello6',null,'moron6');
+      Signal.connectOnce(null,'sayHello6',null,'moron6');
+
+
+//   7. Simple Test 7 connect via objects
+
+
+      var o = {};
+      o.sayHello = function(n) {
+      	alert("Hello! " + n + " (from object o)");
+      }
+      var m = {};
+      m.moron = function(n) {
+      	alert(n + " is a stupid head (from object m)");
+      }
+
+      Signal.connect(o,'sayHello',m,'moron');
+
+      onclick="o.sayHello('Fred')"
+
+
+//   8. Simple Test 8 connect but the insult comes first using {before:true}
+
+
+      Signal.connect(null,'sayHello8',null,'moron8', {before:true});
+
+
+//   9. Simple Test 9 connect but the insult is mutated
+
+
+      Signal.connect(null,'sayHello9',null,'moron9', {mutate:function() { return ['smelly ' + arguments[0]] }});
+
+ */
+
+
+/**
+ * @class String extensions
+ */
+Object.extend(String.prototype, 
+{
+	/**
+	 * @param {String} "left" to pad the string on the left, "right" to pad right.
+	 * @param {Number} minimum string length.
+	 * @param {String} character(s) to pad 
+	 * @return {String} padded character(s) on the left or right to satisfy minimum string length
+	 */
+
+	pad : function(side, len, chr) {
+		if (!chr) chr = ' ';
+		var s = this;
+		var left = side.toLowerCase()=='left';
+		while (s.length<len) s = left? chr + s : s + chr;
+		return s;
+	},
+
+	/**
+	 * @param {Number} minimum string length.
+	 * @param {String} character(s) to pad 
+	 * @return {String} padded character(s) on the left to satisfy minimum string length
+	 */
+	padLeft : function(len, chr) {
+		return this.pad('left',len,chr);
+	},
+
+	/**
+	 * @param {Number} minimum string length.
+	 * @param {String} character(s) to pad 
+	 * @return {String} padded character(s) on the right to satisfy minimum string length
+	 */
+	padRight : function(len, chr) {
+		return this.pad('right',len,chr);
+	},
+
+	/**
+	 * @param {Number} minimum string length.
+	 * @return {String} append zeros to the left to satisfy minimum string length.
+	 */
+	zerofill : function(len) { 
+		return this.padLeft(len,'0');
+	},
+
+	/**
+	 * @return {String} removed white spaces from both ends.
+	 */
+	trim : function() { 
+		return this.replace(/^\s+|\s+$/g,'');
+	},
+
+	/**
+	 * @return {String} removed white spaces from the left end.
+	 */
+	trimLeft : function() { 
+		return this.replace(/^\s+/,''); 
+	},
+
+	/**
+	 * @return {String} removed white spaces from the right end.
+	 */
+	trimRight : function() { 
+		return this.replace(/\s+$/,'');
+	},
+
+	/**
+	 * Convert period separated function names into a function reference.
+	 * e.g. "Prado.AJAX.Callback.Action.setValue".toFunction() will return
+	 * the actual function Prado.AJAX.Callback.Action.setValue()
+	 * @return {Function} the corresponding function represented by the string.
+	 */
+	toFunction : function()
+	{
+		var commands = this.split(/\./);
+		var command = window;
+		commands.each(function(action)
+		{ 
+			if(command[new String(action)]) 
+				command=command[new String(action)]; 
+		});
+		if(typeof(command) == "function")
+			return command;
+		else
+		{
+			if(typeof Logger != "undefined")
+				Logger.error("Missing function", this);
+				
+			throw new Error	("Missing function '"+this+"'");
+		}
+	},
+
+	/** 
+	 * Convert a string into integer, returns null if not integer.
+	 * @return {Number} null if string does not represent an integer.
+	 */
+	toInteger : function()
+	{
+		var exp = /^\s*[-\+]?\d+\s*$/;
+		if (this.match(exp) == null)
+			return null;
+		var num = parseInt(this, 10);
+		return (isNaN(num) ? null : num);
+	},
+
+	/** 
+	 * Convert a string into a double/float value. <b>Internationalization 
+	 * is not supported</b>
+	 * @param {String} the decimal character
+	 * @return {Double} null if string does not represent a float value
+	 */
+	toDouble : function(decimalchar)
+	{
+		if(this.length <= 0) return null;
+		decimalchar = decimalchar || ".";
+		var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$");
+		var m = this.match(exp);
+		
+		if (m == null)	
+			return null;
+		m[1] = m[1] || "";
+		m[2] = m[2] || "0";
+		m[4] = m[4] || "0";
+				
+		var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
+		var num = parseFloat(cleanInput);
+		return (isNaN(num) ? null : num);
+	},
+
+	/**
+	 * Convert strings that represent a currency value (e.g. a float with grouping 
+	 * characters) to float. E.g. "10,000.50" will become "10000.50". The number 
+	 * of dicimal digits, grouping and decimal characters can be specified.
+	 * <i>The currency input format is <b>very</b> strict, null will be returned if
+	 * the pattern does not match</i>.
+	 * @param {String} the grouping character, default is ","
+	 * @param {Number} number of decimal digits
+	 * @param {String} the decimal character, default is "."
+	 * @type {Double} the currency value as float.
+	 */
+	toCurrency : function(groupchar, digits, decimalchar)
+	{
+		groupchar = groupchar || ",";
+		decimalchar = decimalchar || ".";
+		digits = typeof(digits) == "undefined" ? 2 : digits;
 
-Object.extend(Builder,
+		var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)"
+			+ ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "")
+			+ "\\s*$");
+		var m = this.match(exp);
+		if (m == null)
+			return null;
+		var intermed = m[2] + m[5] ;
+		var cleanInput = m[1] + intermed.replace(
+				new RegExp("(\\" + groupchar + ")", "g"), "") 
+								+ ((digits > 0) ? "." + m[7] : "");
+		var num = parseFloat(cleanInput);
+		return (isNaN(num) ? null : num);
+	},
+
+	/**
+	 * Converts the string to a date by finding values that matches the 
+	 * date format pattern.
+	 * @param string date format pattern, e.g. MM-dd-yyyy
+	 * @return {Date} the date extracted from the string
+	 */
+	toDate : function(format)
+	{
+		return Date.SimpleParse(this, format);
+	}
+});
+
+/**
+ * @class Event extensions.
+ */
+Object.extend(Event,
 {
-	exportTags:function()
+	/**
+	 * Register a function to be executed when the page is loaded.
+	 * Note that the page is only loaded if all resources (e.g. images)
+	 * are loaded.
+	 *
+	 * Example: Show an alert box with message "Page Loaded!" when the
+	 * page finished loading.
+	 * <code>
+	 * Event.OnLoad(function(){ alert("Page Loaded!"); });
+	 * </code>
+	 *
+	 * @param {Function} function to execute when page is loaded.
+	 */
+	OnLoad : function (fn)
 	{
-		var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"];
-		tags.each(function(tag)
-		{
-			window[tag]=function()
-			{
-				var args=$A(arguments);
-				if(args.length==0)
-					return Builder.node(tag,null);
-				if(args.length==1)
-					return Builder.node(tag,args[0]);
-				if(args.length>1)
-					return Builder.node(tag,args.shift(),args);
+		// opera onload is in document, not window
+		var w = document.addEventListener &&
+					!window.addEventListener ? document : window;
+		Event.observe(w,'load',fn);
+	},
 
-			};
-		});
-	}
-});
+	/**
+	 * @param {Event} a keyboard event
+	 * @return {Number} the Unicode character code generated by the key
+	 * that was struck.
+	 */
+	keyCode : function(e)
+	{
+	   return e.keyCode != null ? e.keyCode : e.charCode
+	},
 
-Builder.exportTags();
-
+	/**
+	 * @param {String} event type or event name.
+	 * @return {Boolean} true if event type is of HTMLEvent, false
+	 * otherwise
+	 */
+	isHTMLEvent : function(type)
+	{
+		var events = ['abort', 'blur', 'change', 'error', 'focus',
+					'load', 'reset', 'resize', 'scroll', 'select',
+					'submit', 'unload'];
+		return events.include(type);
+	},
+
+	/**
+	 * @param {String} event type or event name
+	 * @return {Boolean} true if event type is of MouseEvent,
+	 * false otherwise
+	 */
+	isMouseEvent : function(type)
+	{
+		var events = ['click', 'mousedown', 'mousemove', 'mouseout',
+					'mouseover', 'mouseup'];
+		return events.include(type);
+	},
+
+	/**
+	 * Dispatch the DOM event of a given <tt>type</tt> on a DOM
+	 * <tt>element</tt>. Only HTMLEvent and MouseEvent can be
+	 * dispatched, keyboard events or UIEvent can not be dispatch
+	 * via javascript consistently.
+	 * For the "submit" event the submit() method is called.
+	 * @param {Object} element id string or a DOM element.
+	 * @param {String} event type to dispatch.
+	 */
+	fireEvent : function(element,type,canBubble)
+	{
+		canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+		element = $(element);
+		if(type == "submit")
+			return element.submit();
+		if(document.createEvent)
+        {
+			if(Event.isHTMLEvent(type))
+			{
+				var event = document.createEvent('HTMLEvents');
+	            event.initEvent(type, canBubble, true);
+			}
+			else if(Event.isMouseEvent(type))
+			{
+				var event = document.createEvent('MouseEvents');
+				if (event.initMouseEvent)
+		        {
+					event.initMouseEvent(type,canBubble,true,
+						document.defaultView, 1, 0, 0, 0, 0, false,
+								false, false, false, 0, null);
+		        }
+		        else
+		        {
+		            // Safari
+		            // TODO we should be initialising other mouse-event related attributes here
+		            event.initEvent(type, canBubble, true);
+		        }
+			}
+            element.dispatchEvent(event);
+        }
+        else if(document.createEventObject)
+        {
+        	var evObj = document.createEventObject();
+            element.fireEvent('on'+type, evObj);
+        }
+        else if(typeof(element['on'+type]) == "function")
+            element['on'+type]();
+	}
+});
 
 
 Object.extend(Date.prototype,
@@ -2661,6 +4119,32 @@ Object.extend(Date,
 });
 
 
+Object.extend(Builder,
+{
+	exportTags:function()
+	{
+		var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"];
+		tags.each(function(tag)
+		{
+			window[tag]=function()
+			{
+				var args=$A(arguments);
+				if(args.length==0)
+					return Builder.node(tag,null);
+				if(args.length==1)
+					return Builder.node(tag,args[0]);
+				if(args.length>1)
+					return Builder.node(tag,args.shift(),args);
+
+			};
+		});
+	}
+});
+
+Builder.exportTags();
+
+
+
 var Prado =
 {
 	Version: '3.1',
@@ -3545,3 +5029,182 @@ Prado.WebUI.TRadioButtonList = Base.extend(
 	}
 });
 
+Prado.WebUI.TRatingList = Base.extend(
+{
+	selectedIndex : -1,
+	rating: -1,
+	enabled : true,
+	readOnly : false,
+
+	constructor : function(options)
+	{
+		var cap = $(options.CaptionID);
+		this.options = Object.extend(
+		{
+			caption : cap ? cap.innerHTML : ''
+		}, options || {});
+
+		Prado.WebUI.TRatingList.register(this);
+		this._init();
+		this.selectedIndex = options.SelectedIndex;
+		this.rating = options.Rating;
+		if(options.Rating <= 0 && options.SelectedIndex >= 0)
+			this.rating = options.SelectedIndex+1;
+		this.showRating(this.rating);
+	},
+
+	_init: function(options)
+	{
+		Element.addClassName($(this.options.ListID),this.options.Style);
+		this.radios = new Array();
+		var index=0;
+		for(var i = 0; i<this.options.ItemCount; i++)
+		{
+			var radio = $(this.options.ListID+'_c'+i);
+			var td = radio.parentNode;
+			if(radio && td.tagName.toLowerCase()=='td')
+			{
+				this.radios.push(radio);
+				Event.observe(td, "mouseover", this.hover.bindEvent(this,index));
+				Event.observe(td, "mouseout", this.recover.bindEvent(this,index));
+				Event.observe(td, "click", this.click.bindEvent(this, index));
+				index++;
+				Element.addClassName(td,"rating");
+			}
+		}
+	},
+
+	hover : function(ev,index)
+	{
+		if(this.enabled==false) return;
+		for(var i = 0; i<this.radios.length; i++)
+		{
+			var node = this.radios[i].parentNode;
+			var action = i <= index ? 'addClassName' : 'removeClassName'
+			Element[action](node,"rating_hover");
+			Element.removeClassName(node,"rating_selected");
+			Element.removeClassName(node,"rating_half");
+		}
+		this.showCaption(this.getIndexCaption(index));
+	},
+
+	recover : function(ev,index)
+	{
+		if(this.enabled==false) return;
+		this.showRating(this.rating);
+		this.showCaption(this.options.caption);
+	},
+
+	click : function(ev, index)
+	{
+		if(this.enabled==false) return;
+		for(var i = 0; i<this.radios.length; i++)
+			this.radios[i].checked = (i == index);
+
+		this.selectedIndex = index;
+		this.setRating(index+1);
+
+		this.dispatchRequest(ev);
+	},
+
+	dispatchRequest : function(ev)
+	{
+		var requestOptions = Object.extend(
+		{
+			ID : this.options.ListID+"_c"+this.selectedIndex,
+			EventTarget : this.options.ListName+"$c"+this.selectedIndex
+		},this.options);
+		var request = new Prado.CallbackRequest(requestOptions.EventTarget, requestOptions);
+		if(request.dispatch()==false)
+			Event.stop(ev);
+	},
+
+	setRating : function(value)
+	{
+		this.rating = value;
+		var base = Math.floor(value-1);
+		var remainder = value - base-1;
+		var halfMax = this.options.HalfRating["1"];
+		var index = remainder > halfMax ? base+1 : base;
+		for(var i = 0; i<this.radios.length; i++)
+			this.radios[i].checked = (i == index);
+
+		var caption = this.getIndexCaption(index);
+		this.setCaption(caption);
+		this.showCaption(caption);
+
+		this.showRating(value);
+	},
+
+	showRating: function(value)
+	{
+		var base = Math.floor(value-1);
+		var remainder = value - base-1;
+		var halfMin = this.options.HalfRating["0"];
+		var halfMax = this.options.HalfRating["1"];
+		var index = remainder > halfMax ? base+1 : base;
+		var hasHalf = remainder >= halfMin && remainder <= halfMax;
+		for(var i = 0; i<this.radios.length; i++)
+		{
+			var node = this.radios[i].parentNode;
+			var action = i > index ? 'removeClassName' : 'addClassName';
+			Element[action](node, "rating_selected");
+			if(i==index+1 && hasHalf)
+				Element.addClassName(node, "rating_half");
+			else
+				Element.removeClassName(node, "rating_half");
+			Element.removeClassName(node,"rating_hover");
+		}
+	},
+
+	getIndexCaption : function(index)
+	{
+		return index > -1 ? this.radios[index].value : this.options.caption;
+	},
+
+	showCaption : function(value)
+	{
+		var caption = $(this.options.CaptionID);
+		if(caption) caption.innerHTML = value;
+		$(this.options.ListID).title = value;
+	},
+
+	setCaption : function(value)
+	{
+		this.options.caption = value;
+		this.showCaption(value);
+	},
+
+	setEnabled : function(value)
+	{
+		this.enabled = value;
+		for(var i = 0; i<this.radios.length; i++)
+		{
+			var action = value ? 'removeClassName' : 'addClassName'
+			Element[action](this.radios[i].parentNode, "rating_disabled");
+		}
+	}
+},
+{
+ratings : {},
+register : function(rating)
+{
+	Prado.WebUI.TRatingList.ratings[rating.options.ListID] = rating;
+},
+
+setEnabled : function(id,value)
+{
+	Prado.WebUI.TRatingList.ratings[id].setEnabled(value);
+},
+
+setRating : function(id,value)
+{
+	Prado.WebUI.TRatingList.ratings[id].setRating(value);
+},
+
+setCaption : function(id,value)
+{
+	Prado.WebUI.TRatingList.ratings[id].setCaption(value);
+}
+});
+
-- 
cgit v1.2.3