diff options
Diffstat (limited to 'framework/Web/Javascripts')
18 files changed, 14423 insertions, 26 deletions
| diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/compressed/ajax.js index dce49a7f..f39f8325 100644 --- a/framework/Web/Javascripts/js/ajax.js +++ b/framework/Web/Javascripts/js/compressed/ajax.js @@ -125,21 +125,22 @@ break;case'n':if(next()=='u'&&next()=='l'&&next()=='l'){next();return null;}  break;}  error("Syntax error");}  function value(){white();switch(ch){case'{':return object();case'[':return array();case'"':return string();case'-':return number();default:return ch>='0'&&ch<='9'?number():word();}} -return value();}};var Autocompleter={} +return value();}};if(typeof Effect=='undefined') +throw("controls.js requires including script.aculo.us' effects.js library");var Autocompleter={}  Autocompleter.Base=function(){};Autocompleter.Base.prototype={baseInitialize:function(element,update,options){this.element=$(element);this.update=$(update);this.hasFocus=false;this.changed=false;this.active=false;this.index=0;this.entryCount=0;if(this.setOptions)  this.setOptions(options);else  this.options=options||{};this.options.paramName=this.options.paramName||this.element.name;this.options.tokens=this.options.tokens||[];this.options.frequency=this.options.frequency||0.4;this.options.minChars=this.options.minChars||1;this.options.onShow=this.options.onShow||function(element,update){if(!update.style.position||update.style.position=='absolute'){update.style.position='absolute';Position.clone(element,update,{setHeight:false,offsetTop:element.offsetHeight});}  Effect.Appear(update,{duration:0.15});};this.options.onHide=this.options.onHide||function(element,update){new Effect.Fade(update,{duration:0.15})};if(typeof(this.options.tokens)=='string')  this.options.tokens=new Array(this.options.tokens);this.observer=null;this.element.setAttribute('autocomplete','off');Element.hide(this.update);Event.observe(this.element,"blur",this.onBlur.bindAsEventListener(this));Event.observe(this.element,"keypress",this.onKeyPress.bindAsEventListener(this));},show:function(){if(Element.getStyle(this.update,'display')=='none')this.options.onShow(this.element,this.update);if(!this.iefix&&(navigator.appVersion.indexOf('MSIE')>0)&&(navigator.userAgent.indexOf('Opera')<0)&&(Element.getStyle(this.update,'position')=='absolute')){new Insertion.After(this.update,'<iframe id="'+this.update.id+'_iefix" '+'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" '+'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');this.iefix=$(this.update.id+'_iefix');} -if(this.iefix)setTimeout(this.fixIEOverlapping.bind(this),50);},fixIEOverlapping:function(){Position.clone(this.update,this.iefix);this.iefix.style.zIndex=1;this.update.style.zIndex=2;Element.show(this.iefix);},hide:function(){this.stopIndicator();if(Element.getStyle(this.update,'display')!='none')this.options.onHide(this.element,this.update);if(this.iefix)Element.hide(this.iefix);},startIndicator:function(){if(this.options.indicator)Element.show(this.options.indicator);},stopIndicator:function(){if(this.options.indicator)Element.hide(this.options.indicator);},onKeyPress:function(event){if(this.active) +if(this.iefix)setTimeout(this.fixIEOverlapping.bind(this),50);},fixIEOverlapping:function(){Position.clone(this.update,this.iefix,{setTop:(!this.update.style.height)});this.iefix.style.zIndex=1;this.update.style.zIndex=2;Element.show(this.iefix);},hide:function(){this.stopIndicator();if(Element.getStyle(this.update,'display')!='none')this.options.onHide(this.element,this.update);if(this.iefix)Element.hide(this.iefix);},startIndicator:function(){if(this.options.indicator)Element.show(this.options.indicator);},stopIndicator:function(){if(this.options.indicator)Element.hide(this.options.indicator);},onKeyPress:function(event){if(this.active)  switch(event.keyCode){case Event.KEY_TAB:case Event.KEY_RETURN:this.selectEntry();Event.stop(event);case Event.KEY_ESC:this.hide();this.active=false;Event.stop(event);return;case Event.KEY_LEFT:case Event.KEY_RIGHT:return;case Event.KEY_UP:this.markPrevious();this.render();if(navigator.appVersion.indexOf('AppleWebKit')>0)Event.stop(event);return;case Event.KEY_DOWN:this.markNext();this.render();if(navigator.appVersion.indexOf('AppleWebKit')>0)Event.stop(event);return;}  else  if(event.keyCode==Event.KEY_TAB||event.keyCode==Event.KEY_RETURN||(navigator.appVersion.indexOf('AppleWebKit')>0&&event.keyCode==0))return;this.changed=true;this.hasFocus=true;if(this.observer)clearTimeout(this.observer);this.observer=setTimeout(this.onObserverEvent.bind(this),this.options.frequency*1000);},activate:function(){this.changed=false;this.hasFocus=true;this.getUpdatedChoices();},onHover:function(event){var element=Event.findElement(event,'LI');if(this.index!=element.autocompleteIndex)  {this.index=element.autocompleteIndex;this.render();}  Event.stop(event);},onClick:function(event){var element=Event.findElement(event,'LI');this.index=element.autocompleteIndex;this.selectEntry();this.hide();},onBlur:function(event){setTimeout(this.hide.bind(this),250);this.hasFocus=false;this.active=false;},render:function(){if(this.entryCount>0){for(var i=0;i<this.entryCount;i++)  this.index==i?Element.addClassName(this.getEntry(i),"selected"):Element.removeClassName(this.getEntry(i),"selected");if(this.hasFocus){this.show();this.active=true;}}else{this.active=false;this.hide();}},markPrevious:function(){if(this.index>0)this.index-- -else this.index=this.entryCount-1;},markNext:function(){if(this.index<this.entryCount-1)this.index++ -else this.index=0;},getEntry:function(index){return this.update.firstChild.childNodes[index];},getCurrentEntry:function(){return this.getEntry(this.index);},selectEntry:function(){this.active=false;this.updateElement(this.getCurrentEntry());},updateElement:function(selectedElement){if(this.options.updateElement){this.options.updateElement(selectedElement);return;} +else this.index=this.entryCount-1;this.getEntry(this.index).scrollIntoView(true);},markNext:function(){if(this.index<this.entryCount-1)this.index++ +else this.index=0;this.getEntry(this.index).scrollIntoView(false);},getEntry:function(index){return this.update.firstChild.childNodes[index];},getCurrentEntry:function(){return this.getEntry(this.index);},selectEntry:function(){this.active=false;this.updateElement(this.getCurrentEntry());},updateElement:function(selectedElement){if(this.options.updateElement){this.options.updateElement(selectedElement);return;}  var value='';if(this.options.select){var nodes=document.getElementsByClassName(this.options.select,selectedElement)||[];if(nodes.length>0)value=Element.collectTextNodes(nodes[0],this.options.select);}else  value=Element.collectTextNodesIgnoreClass(selectedElement,'informal');var lastTokenPos=this.findLastToken();if(lastTokenPos!=-1){var newValue=this.element.value.substr(0,lastTokenPos+1);var whitespace=this.element.value.substr(lastTokenPos+1).match(/^\s+/);if(whitespace)  newValue+=whitespace[0];this.element.value=newValue+value;}else{this.element.value=value;} @@ -163,7 +164,7 @@ Ajax.InPlaceEditor=Class.create();Ajax.InPlaceEditor.defaultHighlightColor="#FFF  if(this.options.externalControl){this.options.externalControl=$(this.options.externalControl);}  this.originalBackground=Element.getStyle(this.element,'background-color');if(!this.originalBackground){this.originalBackground="transparent";}  this.element.title=this.options.clickToEditText;this.onclickListener=this.enterEditMode.bindAsEventListener(this);this.mouseoverListener=this.enterHover.bindAsEventListener(this);this.mouseoutListener=this.leaveHover.bindAsEventListener(this);Event.observe(this.element,'click',this.onclickListener);Event.observe(this.element,'mouseover',this.mouseoverListener);Event.observe(this.element,'mouseout',this.mouseoutListener);if(this.options.externalControl){Event.observe(this.options.externalControl,'click',this.onclickListener);Event.observe(this.options.externalControl,'mouseover',this.mouseoverListener);Event.observe(this.options.externalControl,'mouseout',this.mouseoutListener);}},enterEditMode:function(evt){if(this.saving)return;if(this.editing)return;this.editing=true;this.onEnterEditMode();if(this.options.externalControl){Element.hide(this.options.externalControl);} -Element.hide(this.element);this.createForm();this.element.parentNode.insertBefore(this.form,this.element);Field.scrollFreeActivate(this.editField);if(evt){Event.stop(evt);} +Element.hide(this.element);this.createForm();this.element.parentNode.insertBefore(this.form,this.element);if(!this.options.loadTextURL)Field.scrollFreeActivate(this.editField);if(evt){Event.stop(evt);}  return false;},createForm:function(){this.form=document.createElement("form");this.form.id=this.options.formId;Element.addClassName(this.form,this.options.formClassName)  this.form.onsubmit=this.onSubmit.bind(this);this.createEditField();if(this.options.textarea){var br=document.createElement("br");this.form.appendChild(br);}  if(this.options.okButton){okButton=document.createElement("input");okButton.type="submit";okButton.value=this.options.okText;okButton.className='editor_ok_button';this.form.appendChild(okButton);} @@ -172,7 +173,7 @@ var obj=this;if(this.options.rows==1&&!this.hasHTMLLineBreaks(text)){this.option  textField.onblur=this.onSubmit.bind(this);this.editField=textField;}else{this.options.textarea=true;var textArea=document.createElement("textarea");textArea.obj=this;textArea.name="value";textArea.value=this.convertHTMLLineBreaks(text);textArea.rows=this.options.rows;textArea.cols=this.options.cols||40;textArea.className='editor_field';if(this.options.submitOnBlur)  textArea.onblur=this.onSubmit.bind(this);this.editField=textArea;}  if(this.options.loadTextURL){this.loadExternalText();} -this.form.appendChild(this.editField);},getText:function(){return this.element.innerHTML;},loadExternalText:function(){Element.addClassName(this.form,this.options.loadingClassName);this.editField.disabled=true;new Ajax.Request(this.options.loadTextURL,Object.extend({asynchronous:true,onComplete:this.onLoadedExternalText.bind(this)},this.options.ajaxOptions));},onLoadedExternalText:function(transport){Element.removeClassName(this.form,this.options.loadingClassName);this.editField.disabled=false;this.editField.value=transport.responseText.stripTags();},onclickCancel:function(){this.onComplete();this.leaveEditMode();return false;},onFailure:function(transport){this.options.onFailure(transport);if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML;this.oldInnerHTML=null;} +this.form.appendChild(this.editField);},getText:function(){return this.element.innerHTML;},loadExternalText:function(){Element.addClassName(this.form,this.options.loadingClassName);this.editField.disabled=true;new Ajax.Request(this.options.loadTextURL,Object.extend({asynchronous:true,onComplete:this.onLoadedExternalText.bind(this)},this.options.ajaxOptions));},onLoadedExternalText:function(transport){Element.removeClassName(this.form,this.options.loadingClassName);this.editField.disabled=false;this.editField.value=transport.responseText.stripTags();Field.scrollFreeActivate(this.editField);},onclickCancel:function(){this.onComplete();this.leaveEditMode();return false;},onFailure:function(transport){this.options.onFailure(transport);if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML;this.oldInnerHTML=null;}  return false;},onSubmit:function(){var form=this.form;var value=this.editField.value;this.onLoading();if(this.options.evalScripts){new Ajax.Request(this.url,Object.extend({parameters:this.options.callback(form,value),onComplete:this.onComplete.bind(this),onFailure:this.onFailure.bind(this),asynchronous:true,evalScripts:true},this.options.ajaxOptions));}else{new Ajax.Updater({success:this.element,failure:null},this.url,Object.extend({parameters:this.options.callback(form,value),onComplete:this.onComplete.bind(this),onFailure:this.onFailure.bind(this)},this.options.ajaxOptions));}  if(arguments.length>1){Event.stop(arguments[0]);}  return false;},onLoading:function(){this.saving=true;this.removeForm();this.leaveHover();this.showSaving();},showSaving:function(){this.oldInnerHTML=this.element.innerHTML;this.element.innerHTML=this.options.savingText;Element.addClassName(this.element,this.options.savingClassName);this.element.style.backgroundColor=this.originalBackground;Element.show(this.element);},removeForm:function(){if(this.form){if(this.form.parentNode)Element.remove(this.form);this.form=null;}},enterHover:function(){if(this.saving)return;this.element.style.backgroundColor=this.options.highlightcolor;if(this.effect){this.effect.cancel();} @@ -181,7 +182,8 @@ Element.removeClassName(this.element,this.options.hoverClassName)  if(this.saving)return;this.effect=new Effect.Highlight(this.element,{startcolor:this.options.highlightcolor,endcolor:this.options.highlightendcolor,restorecolor:this.originalBackground});},leaveEditMode:function(){Element.removeClassName(this.element,this.options.savingClassName);this.removeForm();this.leaveHover();this.element.style.backgroundColor=this.originalBackground;Element.show(this.element);if(this.options.externalControl){Element.show(this.options.externalControl);}  this.editing=false;this.saving=false;this.oldInnerHTML=null;this.onLeaveEditMode();},onComplete:function(transport){this.leaveEditMode();this.options.onComplete.bind(this)(transport,this.element);},onEnterEditMode:function(){},onLeaveEditMode:function(){},dispose:function(){if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML;}  this.leaveEditMode();Event.stopObserving(this.element,'click',this.onclickListener);Event.stopObserving(this.element,'mouseover',this.mouseoverListener);Event.stopObserving(this.element,'mouseout',this.mouseoutListener);if(this.options.externalControl){Event.stopObserving(this.options.externalControl,'click',this.onclickListener);Event.stopObserving(this.options.externalControl,'mouseover',this.mouseoverListener);Event.stopObserving(this.options.externalControl,'mouseout',this.mouseoutListener);}}};Ajax.InPlaceCollectionEditor=Class.create();Object.extend(Ajax.InPlaceCollectionEditor.prototype,Ajax.InPlaceEditor.prototype);Object.extend(Ajax.InPlaceCollectionEditor.prototype,{createEditField:function(){if(!this.cached_selectTag){var selectTag=document.createElement("select");var collection=this.options.collection||[];var optionTag;collection.each(function(e,i){optionTag=document.createElement("option");optionTag.value=(e instanceof Array)?e[0]:e;if(this.options.value==optionTag.value)optionTag.selected=true;optionTag.appendChild(document.createTextNode((e instanceof Array)?e[1]:e));selectTag.appendChild(optionTag);}.bind(this));this.cached_selectTag=selectTag;} -this.editField=this.cached_selectTag;if(this.options.loadTextURL)this.loadExternalText();this.form.appendChild(this.editField);this.options.callback=function(form,value){return"value="+encodeURIComponent(value);}}});Form.Element.DelayedObserver=Class.create();Form.Element.DelayedObserver.prototype={initialize:function(element,delay,callback){this.delay=delay||0.5;this.element=$(element);this.callback=callback;this.timer=null;this.lastValue=$F(this.element);Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));},delayedListener:function(event){if(this.lastValue==$F(this.element))return;if(this.timer)clearTimeout(this.timer);this.timer=setTimeout(this.onTimerEvent.bind(this),this.delay*1000);this.lastValue=$F(this.element);},onTimerEvent:function(){this.timer=null;this.callback(this.element,$F(this.element));}};var Droppables={drops:[],remove:function(element){this.drops=this.drops.reject(function(d){return d.element==$(element)});},add:function(element){element=$(element);var options=Object.extend({greedy:true,hoverclass:null,tree:false},arguments[1]||{});if(options.containment){options._containers=[];var containment=options.containment;if((typeof containment=='object')&&(containment.constructor==Array)){containment.each(function(c){options._containers.push($(c))});}else{options._containers.push($(containment));}} +this.editField=this.cached_selectTag;if(this.options.loadTextURL)this.loadExternalText();this.form.appendChild(this.editField);this.options.callback=function(form,value){return"value="+encodeURIComponent(value);}}});Form.Element.DelayedObserver=Class.create();Form.Element.DelayedObserver.prototype={initialize:function(element,delay,callback){this.delay=delay||0.5;this.element=$(element);this.callback=callback;this.timer=null;this.lastValue=$F(this.element);Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));},delayedListener:function(event){if(this.lastValue==$F(this.element))return;if(this.timer)clearTimeout(this.timer);this.timer=setTimeout(this.onTimerEvent.bind(this),this.delay*1000);this.lastValue=$F(this.element);},onTimerEvent:function(){this.timer=null;this.callback(this.element,$F(this.element));}};if(typeof Effect=='undefined') +throw("dragdrop.js requires including script.aculo.us' effects.js library");var Droppables={drops:[],remove:function(element){this.drops=this.drops.reject(function(d){return d.element==$(element)});},add:function(element){element=$(element);var options=Object.extend({greedy:true,hoverclass:null,tree:false},arguments[1]||{});if(options.containment){options._containers=[];var containment=options.containment;if((typeof containment=='object')&&(containment.constructor==Array)){containment.each(function(c){options._containers.push($(c))});}else{options._containers.push($(containment));}}  if(options.accept)options.accept=[options.accept].flatten();Element.makePositioned(element);options.element=element;this.drops.push(options);},findDeepestChild:function(drops){deepest=drops[0];for(i=1;i<drops.length;++i)  if(Element.isParent(drops[i].element,deepest.element))  deepest=drops[i];return deepest;},isContained:function(element,drop){var containmentNode;if(drop.tree){containmentNode=element.treeNode;}else{containmentNode=element.parentNode;} @@ -197,10 +199,9 @@ var Draggables={drags:[],observers:[],register:function(draggable){if(this.drags  this.drags.push(draggable);},unregister:function(draggable){this.drags=this.drags.reject(function(d){return d==draggable});if(this.drags.length==0){Event.stopObserving(document,"mouseup",this.eventMouseUp);Event.stopObserving(document,"mousemove",this.eventMouseMove);Event.stopObserving(document,"keypress",this.eventKeypress);}},activate:function(draggable){window.focus();this.activeDraggable=draggable;},deactivate:function(){this.activeDraggable=null;},updateDrag:function(event){if(!this.activeDraggable)return;var pointer=[Event.pointerX(event),Event.pointerY(event)];if(this._lastPointer&&(this._lastPointer.inspect()==pointer.inspect()))return;this._lastPointer=pointer;this.activeDraggable.updateDrag(event,pointer);},endDrag:function(event){if(!this.activeDraggable)return;this._lastPointer=null;this.activeDraggable.endDrag(event);this.activeDraggable=null;},keyPress:function(event){if(this.activeDraggable)  this.activeDraggable.keyPress(event);},addObserver:function(observer){this.observers.push(observer);this._cacheObserverCallbacks();},removeObserver:function(element){this.observers=this.observers.reject(function(o){return o.element==element});this._cacheObserverCallbacks();},notify:function(eventName,draggable,event){if(this[eventName+'Count']>0)  this.observers.each(function(o){if(o[eventName])o[eventName](eventName,draggable,event);});},_cacheObserverCallbacks:function(){['onStart','onEnd','onDrag'].each(function(eventName){Draggables[eventName+'Count']=Draggables.observers.select(function(o){return o[eventName];}).length;});}} -var Draggable=Class.create();Draggable.prototype={initialize:function(element){var options=Object.extend({handle:false,starteffect:function(element){element._opacity=Element.getOpacity(element);new Effect.Opacity(element,{duration:0.2,from:element._opacity,to:0.7});},reverteffect:function(element,top_offset,left_offset){var dur=Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;element._revert=new Effect.Move(element,{x:-left_offset,y:-top_offset,duration:dur});},endeffect:function(element){var toOpacity=typeof element._opacity=='number'?element._opacity:1.0 -new Effect.Opacity(element,{duration:0.2,from:0.7,to:toOpacity});},zindex:1000,revert:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,snap:false},arguments[1]||{});this.element=$(element);if(options.handle&&(typeof options.handle=='string')){var h=Element.childrenWithClassName(this.element,options.handle,true);if(h.length>0)this.handle=h[0];} +var Draggable=Class.create();Draggable._revertCache={};Draggable._dragging={};Draggable.prototype={initialize:function(element){var options=Object.extend({handle:false,starteffect:function(element){element._opacity=Element.getOpacity(element);Draggable._dragging[element]=true;new Effect.Opacity(element,{duration:0.2,from:element._opacity,to:0.7});},reverteffect:function(element,top_offset,left_offset){var dur=Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;Draggable._revertCache[element]=new Effect.Move(element,{x:-left_offset,y:-top_offset,duration:dur,queue:{scope:'_draggable',position:'end'}});},endeffect:function(element){var toOpacity=typeof element._opacity=='number'?element._opacity:1.0;new Effect.Opacity(element,{duration:0.2,from:0.7,to:toOpacity,queue:{scope:'_draggable',position:'end'},afterFinish:function(){Draggable._dragging[element]=false}});},zindex:1000,revert:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,snap:false},arguments[1]||{});this.element=$(element);if(options.handle&&(typeof options.handle=='string')){var h=Element.childrenWithClassName(this.element,options.handle,true);if(h.length>0)this.handle=h[0];}  if(!this.handle)this.handle=$(options.handle);if(!this.handle)this.handle=this.element;if(options.scroll&&!options.scroll.scrollTo&&!options.scroll.outerHTML) -options.scroll=$(options.scroll);Element.makePositioned(this.element);this.delta=this.currentDelta();this.options=options;this.dragging=false;this.eventMouseDown=this.initDrag.bindAsEventListener(this);Event.observe(this.handle,"mousedown",this.eventMouseDown);Draggables.register(this);},destroy:function(){Event.stopObserving(this.handle,"mousedown",this.eventMouseDown);Draggables.unregister(this);},currentDelta:function(){return([parseInt(Element.getStyle(this.element,'left')||'0'),parseInt(Element.getStyle(this.element,'top')||'0')]);},initDrag:function(event){if(Event.isLeftClick(event)){var src=Event.element(event);if(src.tagName&&(src.tagName=='INPUT'||src.tagName=='SELECT'||src.tagName=='OPTION'||src.tagName=='BUTTON'||src.tagName=='TEXTAREA'))return;if(this.element._revert){this.element._revert.cancel();this.element._revert=null;} +options.scroll=$(options.scroll);Element.makePositioned(this.element);this.delta=this.currentDelta();this.options=options;this.dragging=false;this.eventMouseDown=this.initDrag.bindAsEventListener(this);Event.observe(this.handle,"mousedown",this.eventMouseDown);Draggables.register(this);},destroy:function(){Event.stopObserving(this.handle,"mousedown",this.eventMouseDown);Draggables.unregister(this);},currentDelta:function(){return([parseInt(Element.getStyle(this.element,'left')||'0'),parseInt(Element.getStyle(this.element,'top')||'0')]);},initDrag:function(event){if(typeof Draggable._dragging[this.element]!=undefined&&Draggable._dragging[this.element])return;if(Event.isLeftClick(event)){var src=Event.element(event);if(src.tagName&&(src.tagName=='INPUT'||src.tagName=='SELECT'||src.tagName=='OPTION'||src.tagName=='BUTTON'||src.tagName=='TEXTAREA'))return;if(Draggable._revertCache[this.element]){Draggable._revertCache[this.element].cancel();Draggable._revertCache[this.element]=null;}  var pointer=[Event.pointerX(event),Event.pointerY(event)];var pos=Position.cumulativeOffset(this.element);this.offset=[0,1].map(function(i){return(pointer[i]-pos[i])});Draggables.activate(this);Event.stop(event);}},startDrag:function(event){this.dragging=true;if(this.options.zindex){this.originalZ=parseInt(Element.getStyle(this.element,'z-index')||0);this.element.style.zIndex=this.options.zindex;}  if(this.options.ghosting){this._clone=this.element.cloneNode(true);Position.absolutize(this.element);this.element.parentNode.insertBefore(this._clone,this.element);}  if(this.options.scroll){if(this.options.scroll==window){var where=this._getWindowScroll(this.options.scroll);this.originalScrollLeft=where.left;this.originalScrollTop=where.top;}else{this.originalScrollLeft=this.options.scroll.scrollLeft;this.originalScrollTop=this.options.scroll.scrollTop;}} @@ -214,7 +215,7 @@ this.options.endeffect(this.element);Draggables.deactivate(this);Droppables.rese  var p=[0,1].map(function(i){return(point[i]-pos[i]-this.offset[i])}.bind(this));if(this.options.snap){if(typeof this.options.snap=='function'){p=this.options.snap(p[0],p[1],this);}else{if(this.options.snap instanceof Array){p=p.map(function(v,i){return Math.round(v/this.options.snap[i])*this.options.snap[i]}.bind(this))}else{p=p.map(function(v){return Math.round(v/this.options.snap)*this.options.snap}.bind(this))}}}  var style=this.element.style;if((!this.options.constraint)||(this.options.constraint=='horizontal'))  style.left=p[0]+"px";if((!this.options.constraint)||(this.options.constraint=='vertical')) -style.top=p[1]+"px";if(style.visibility=="hidden")style.visibility="";},stopScrolling:function(){if(this.scrollInterval){clearInterval(this.scrollInterval);this.scrollInterval=null;Draggables._lastScrollPointer=null;}},startScrolling:function(speed){this.scrollSpeed=[speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];this.lastScrolled=new Date();this.scrollInterval=setInterval(this.scroll.bind(this),10);},scroll:function(){var current=new Date();var delta=current-this.lastScrolled;this.lastScrolled=current;if(this.options.scroll==window){with(this._getWindowScroll(this.options.scroll)){if(this.scrollSpeed[0]||this.scrollSpeed[1]){var d=delta/1000;this.options.scroll.scrollTo(left+d*this.scrollSpeed[0],top+d*this.scrollSpeed[1]);}}}else{this.options.scroll.scrollLeft+=this.scrollSpeed[0]*delta/1000;this.options.scroll.scrollTop+=this.scrollSpeed[1]*delta/1000;} +style.top=p[1]+"px";if(style.visibility=="hidden")style.visibility="";},stopScrolling:function(){if(this.scrollInterval){clearInterval(this.scrollInterval);this.scrollInterval=null;Draggables._lastScrollPointer=null;}},startScrolling:function(speed){if(!(speed[0]||speed[1]))return;this.scrollSpeed=[speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];this.lastScrolled=new Date();this.scrollInterval=setInterval(this.scroll.bind(this),10);},scroll:function(){var current=new Date();var delta=current-this.lastScrolled;this.lastScrolled=current;if(this.options.scroll==window){with(this._getWindowScroll(this.options.scroll)){if(this.scrollSpeed[0]||this.scrollSpeed[1]){var d=delta/1000;this.options.scroll.scrollTo(left+d*this.scrollSpeed[0],top+d*this.scrollSpeed[1]);}}}else{this.options.scroll.scrollLeft+=this.scrollSpeed[0]*delta/1000;this.options.scroll.scrollTop+=this.scrollSpeed[1]*delta/1000;}  Position.prepare();Droppables.show(Draggables._lastPointer,this.element);Draggables.notify('onDrag',this);Draggables._lastScrollPointer=Draggables._lastScrollPointer||$A(Draggables._lastPointer);Draggables._lastScrollPointer[0]+=this.scrollSpeed[0]*delta/1000;Draggables._lastScrollPointer[1]+=this.scrollSpeed[1]*delta/1000;if(Draggables._lastScrollPointer[0]<0)  Draggables._lastScrollPointer[0]=0;if(Draggables._lastScrollPointer[1]<0)  Draggables._lastScrollPointer[1]=0;this.draw(Draggables._lastScrollPointer);if(this.options.change)this.options.change(this);},_getWindowScroll:function(w){var T,L,W,H;with(w.document){if(w.document.documentElement&&documentElement.scrollTop){T=documentElement.scrollTop;L=documentElement.scrollLeft;}else if(w.document.body){T=body.scrollTop;L=body.scrollLeft;} @@ -233,7 +234,7 @@ Element.cleanWhitespace(element);options.draggables=[];options.droppables=[];if(  (this.findElements(element,options)||[]).each(function(e){var handle=options.handle?Element.childrenWithClassName(e,options.handle)[0]:e;options.draggables.push(new Draggable(e,Object.extend(options_for_draggable,{handle:handle})));Droppables.add(e,options_for_droppable);if(options.tree)e.treeNode=element;options.droppables.push(e);});if(options.tree){(Sortable.findTreeElements(element,options)||[]).each(function(e){Droppables.add(e,options_for_tree);e.treeNode=element;options.droppables.push(e);});}  this.sortables[element.id]=options;Draggables.addObserver(new SortableObserver(element,options.onUpdate));},findElements:function(element,options){return Element.findChildren(element,options.only,options.tree?true:false,options.tag);},findTreeElements:function(element,options){return Element.findChildren(element,options.only,options.tree?true:false,options.treeTag);},onHover:function(element,dropon,overlap){if(Element.isParent(dropon,element))return;if(overlap>.33&&overlap<.66&&Sortable.options(dropon).tree){return;}else if(overlap>0.5){Sortable.mark(dropon,'before');if(dropon.previousSibling!=element){var oldParentNode=element.parentNode;element.style.visibility="hidden";dropon.parentNode.insertBefore(element,dropon);if(dropon.parentNode!=oldParentNode)  Sortable.options(oldParentNode).onChange(element);Sortable.options(dropon.parentNode).onChange(element);}}else{Sortable.mark(dropon,'after');var nextElement=dropon.nextSibling||null;if(nextElement!=element){var oldParentNode=element.parentNode;element.style.visibility="hidden";dropon.parentNode.insertBefore(element,nextElement);if(dropon.parentNode!=oldParentNode) -Sortable.options(oldParentNode).onChange(element);Sortable.options(dropon.parentNode).onChange(element);}}},onEmptyHover:function(element,dropon,overlap){var oldParentNode=element.parentNode;var droponOptions=Sortable.options(dropon);if(!Element.isParent(dropon,element)){var index;var children=Sortable.findElements(dropon,{tag:droponOptions.tag});var child=null;if(children){var offset=Element.offsetSize(dropon,droponOptions.overlap)*(1.0-overlap);for(index=0;index<children.length;index+=1){if(offset-Element.offsetSize(children[index],droponOptions.overlap)>=0){offset-=Element.offsetSize(children[index],droponOptions.overlap);}else if(offset-(Element.offsetSize(children[index],droponOptions.overlap)/2)>=0){child=index+1<children.length?children[index+1]:null;break;}else{child=children[index];break;}}} +Sortable.options(oldParentNode).onChange(element);Sortable.options(dropon.parentNode).onChange(element);}}},onEmptyHover:function(element,dropon,overlap){var oldParentNode=element.parentNode;var droponOptions=Sortable.options(dropon);if(!Element.isParent(dropon,element)){var index;var children=Sortable.findElements(dropon,{tag:droponOptions.tag,only:droponOptions.only});var child=null;if(children){var offset=Element.offsetSize(dropon,droponOptions.overlap)*(1.0-overlap);for(index=0;index<children.length;index+=1){if(offset-Element.offsetSize(children[index],droponOptions.overlap)>=0){offset-=Element.offsetSize(children[index],droponOptions.overlap);}else if(offset-(Element.offsetSize(children[index],droponOptions.overlap)/2)>=0){child=index+1<children.length?children[index+1]:null;break;}else{child=children[index];break;}}}  dropon.insertBefore(element,child);Sortable.options(oldParentNode).onChange(element);droponOptions.onChange(element);}},unmark:function(){if(Sortable._marker)Element.hide(Sortable._marker);},mark:function(dropon,position){var sortable=Sortable.options(dropon.parentNode);if(sortable&&!sortable.ghosting)return;if(!Sortable._marker){Sortable._marker=$('dropmarker')||document.createElement('DIV');Element.hide(Sortable._marker);Element.addClassName(Sortable._marker,'dropmarker');Sortable._marker.style.position='absolute';document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);}  var offsets=Position.cumulativeOffset(dropon);Sortable._marker.style.left=offsets[0]+'px';Sortable._marker.style.top=offsets[1]+'px';if(position=='after')  if(sortable.overlap=='horizontal') @@ -247,7 +248,7 @@ for(var i=0;i<element.childNodes.length;++i)  if(element.childNodes[i].tagName==containerTag)  return element.childNodes[i];return null;},tree:function(element){element=$(element);var sortableOptions=this.options(element);var options=Object.extend({tag:sortableOptions.tag,treeTag:sortableOptions.treeTag,only:sortableOptions.only,name:element.id,format:sortableOptions.format},arguments[1]||{});var root={id:null,parent:null,children:new Array,container:element,position:0}  return Sortable._tree(element,options,root);},_constructIndex:function(node){var index='';do{if(node.id)index='['+node.position+']'+index;}while((node=node.parent)!=null);return index;},sequence:function(element){element=$(element);var options=Object.extend(this.options(element),arguments[1]||{});return $(this.findElements(element,options)||[]).map(function(item){return item.id.match(options.format)?item.id.match(options.format)[1]:'';});},setSequence:function(element,new_sequence){element=$(element);var options=Object.extend(this.options(element),arguments[2]||{});var nodeMap={};this.findElements(element,options).each(function(n){if(n.id.match(options.format)) -nodeMap[n.id.match(options.format)[1]]=[n,n.parentNode];n.parentNode.removeChild(n);});new_sequence.each(function(ident){var n=nodeMap[ident];if(n){n[1].appendChild(n[0]);delete nodeMap[ident];}});},serialize:function(element){element=$(element);var options=Object.extend(Sortable.options(element),arguments[1]||{});var name=encodeURIComponent((arguments[1]&&arguments[1].name)?arguments[1].name:element.id);if(options.tree){return Sortable.tree(element,arguments[1]).children.map(function(item){return[name+Sortable._constructIndex(item)+"="+ +nodeMap[n.id.match(options.format)[1]]=[n,n.parentNode];n.parentNode.removeChild(n);});new_sequence.each(function(ident){var n=nodeMap[ident];if(n){n[1].appendChild(n[0]);delete nodeMap[ident];}});},serialize:function(element){element=$(element);var options=Object.extend(Sortable.options(element),arguments[1]||{});var name=encodeURIComponent((arguments[1]&&arguments[1].name)?arguments[1].name:element.id);if(options.tree){return Sortable.tree(element,arguments[1]).children.map(function(item){return[name+Sortable._constructIndex(item)+"[id]="+  encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));}).flatten().join('&');}else{return Sortable.sequence(element,arguments[1]).map(function(item){return name+"[]="+encodeURIComponent(item);}).join('&');}}}  Element.isParent=function(child,element){if(!child.parentNode||child==element)return false;if(child.parentNode==element)return true;return Element.isParent(child.parentNode,element);}  Element.findChildren=function(element,only,recursive,tagName){if(!element.hasChildNodes())return null;tagName=tagName.toUpperCase();if(only)only=[only].flatten();var elements=[];$A(element.childNodes).each(function(e){if(e.tagName&&e.tagName.toUpperCase()==tagName&&(!only||(Element.classNames(e).detect(function(v){return only.include(v)})))) @@ -256,16 +257,16 @@ Element.offsetSize=function(element,type){if(type=='vertical'||type=='height')  return element.offsetHeight;else  return element.offsetWidth;}  if(!Control)var Control={};Control.Slider=Class.create();Control.Slider.prototype={initialize:function(handle,track,options){var slider=this;if(handle instanceof Array){this.handles=handle.collect(function(e){return $(e)});}else{this.handles=[$(handle)];} -this.track=$(track);this.options=options||{};this.axis=this.options.axis||'horizontal';this.increment=this.options.increment||1;this.step=parseInt(this.options.step||'1');this.range=this.options.range||$R(0,1);this.value=0;this.values=this.handles.map(function(){return 0});this.spans=this.options.spans?this.options.spans.map(function(s){return $(s)}):false;this.options.startSpan=$(this.options.startSpan||null);this.options.endSpan=$(this.options.endSpan||null);this.restricted=this.options.restricted||false;this.maximum=this.options.maximum||this.range.end;this.minimum=this.options.minimum||this.range.start;this.alignX=parseInt(this.options.alignX||'0');this.alignY=parseInt(this.options.alignY||'0');this.trackLength=this.maximumOffset()-this.minimumOffset();this.handleLength=this.isVertical()?this.handles[0].offsetHeight:this.handles[0].offsetWidth;this.active=false;this.dragging=false;this.disabled=false;if(this.options.disabled)this.setDisabled();this.allowedValues=this.options.values?this.options.values.sortBy(Prototype.K):false;if(this.allowedValues){this.minimum=this.allowedValues.min();this.maximum=this.allowedValues.max();} +this.track=$(track);this.options=options||{};this.axis=this.options.axis||'horizontal';this.increment=this.options.increment||1;this.step=parseInt(this.options.step||'1');this.range=this.options.range||$R(0,1);this.value=0;this.values=this.handles.map(function(){return 0});this.spans=this.options.spans?this.options.spans.map(function(s){return $(s)}):false;this.options.startSpan=$(this.options.startSpan||null);this.options.endSpan=$(this.options.endSpan||null);this.restricted=this.options.restricted||false;this.maximum=this.options.maximum||this.range.end;this.minimum=this.options.minimum||this.range.start;this.alignX=parseInt(this.options.alignX||'0');this.alignY=parseInt(this.options.alignY||'0');this.trackLength=this.maximumOffset()-this.minimumOffset();this.handleLength=this.isVertical()?(this.handles[0].offsetHeight!=0?this.handles[0].offsetHeight:this.handles[0].style.height.replace(/px$/,"")):(this.handles[0].offsetWidth!=0?this.handles[0].offsetWidth:this.handles[0].style.width.replace(/px$/,""));this.active=false;this.dragging=false;this.disabled=false;if(this.options.disabled)this.setDisabled();this.allowedValues=this.options.values?this.options.values.sortBy(Prototype.K):false;if(this.allowedValues){this.minimum=this.allowedValues.min();this.maximum=this.allowedValues.max();}  this.eventMouseDown=this.startDrag.bindAsEventListener(this);this.eventMouseUp=this.endDrag.bindAsEventListener(this);this.eventMouseMove=this.update.bindAsEventListener(this);this.handles.each(function(h,i){i=slider.handles.length-1-i;slider.setValue(parseFloat((slider.options.sliderValue instanceof Array?slider.options.sliderValue[i]:slider.options.sliderValue)||slider.range.start),i);Element.makePositioned(h);Event.observe(h,"mousedown",slider.eventMouseDown);});Event.observe(this.track,"mousedown",this.eventMouseDown);Event.observe(document,"mouseup",this.eventMouseUp);Event.observe(document,"mousemove",this.eventMouseMove);this.initialized=true;},dispose:function(){var slider=this;Event.stopObserving(this.track,"mousedown",this.eventMouseDown);Event.stopObserving(document,"mouseup",this.eventMouseUp);Event.stopObserving(document,"mousemove",this.eventMouseMove);this.handles.each(function(h){Event.stopObserving(h,"mousedown",slider.eventMouseDown);});},setDisabled:function(){this.disabled=true;},setEnabled:function(){this.disabled=false;},getNearestValue:function(value){if(this.allowedValues){if(value>=this.allowedValues.max())return(this.allowedValues.max());if(value<=this.allowedValues.min())return(this.allowedValues.min());var offset=Math.abs(this.allowedValues[0]-value);var newValue=this.allowedValues[0];this.allowedValues.each(function(v){var currentOffset=Math.abs(v-value);if(currentOffset<=offset){newValue=v;offset=currentOffset;}});return newValue;} -if(value>this.range.end)return this.range.end;if(value<this.range.start)return this.range.start;return value;},setValue:function(sliderValue,handleIdx){if(!this.active){this.activeHandle=this.handles[handleIdx];this.activeHandleIdx=handleIdx;this.updateStyles();} +if(value>this.range.end)return this.range.end;if(value<this.range.start)return this.range.start;return value;},setValue:function(sliderValue,handleIdx){if(!this.active){this.activeHandleIdx=handleIdx||0;this.activeHandle=this.handles[this.activeHandleIdx];this.updateStyles();}  handleIdx=handleIdx||this.activeHandleIdx||0;if(this.initialized&&this.restricted){if((handleIdx>0)&&(sliderValue<this.values[handleIdx-1]))  sliderValue=this.values[handleIdx-1];if((handleIdx<(this.handles.length-1))&&(sliderValue>this.values[handleIdx+1]))  sliderValue=this.values[handleIdx+1];} -sliderValue=this.getNearestValue(sliderValue);this.values[handleIdx]=sliderValue;this.value=this.values[0];this.handles[handleIdx].style[this.isVertical()?'top':'left']=this.translateToPx(sliderValue);this.drawSpans();if(!this.dragging||!this.event)this.updateFinished();},setValueBy:function(delta,handleIdx){this.setValue(this.values[handleIdx||this.activeHandleIdx||0]+delta,handleIdx||this.activeHandleIdx||0);},translateToPx:function(value){return Math.round(((this.trackLength-this.handleLength)/(this.range.end-this.range.start))*(value-this.range.start))+"px";},translateToValue:function(offset){return((offset/(this.trackLength-this.handleLength)*(this.range.end-this.range.start))+this.range.start);},getRange:function(range){var v=this.values.sortBy(Prototype.K);range=range||0;return $R(v[range],v[range+1]);},minimumOffset:function(){return(this.isVertical()?this.alignY:this.alignX);},maximumOffset:function(){return(this.isVertical()?this.track.offsetHeight-this.alignY:this.track.offsetWidth-this.alignX);},isVertical:function(){return(this.axis=='vertical');},drawSpans:function(){var slider=this;if(this.spans) +sliderValue=this.getNearestValue(sliderValue);this.values[handleIdx]=sliderValue;this.value=this.values[0];this.handles[handleIdx].style[this.isVertical()?'top':'left']=this.translateToPx(sliderValue);this.drawSpans();if(!this.dragging||!this.event)this.updateFinished();},setValueBy:function(delta,handleIdx){this.setValue(this.values[handleIdx||this.activeHandleIdx||0]+delta,handleIdx||this.activeHandleIdx||0);},translateToPx:function(value){return Math.round(((this.trackLength-this.handleLength)/(this.range.end-this.range.start))*(value-this.range.start))+"px";},translateToValue:function(offset){return((offset/(this.trackLength-this.handleLength)*(this.range.end-this.range.start))+this.range.start);},getRange:function(range){var v=this.values.sortBy(Prototype.K);range=range||0;return $R(v[range],v[range+1]);},minimumOffset:function(){return(this.isVertical()?this.alignY:this.alignX);},maximumOffset:function(){return(this.isVertical()?(this.track.offsetHeight!=0?this.track.offsetHeight:this.track.style.height.replace(/px$/,""))-this.alignY:(this.track.offsetWidth!=0?this.track.offsetWidth:this.track.style.width.replace(/px$/,""))-this.alignY);},isVertical:function(){return(this.axis=='vertical');},drawSpans:function(){var slider=this;if(this.spans)  $R(0,this.spans.length-1).each(function(r){slider.setSpan(slider.spans[r],slider.getRange(r))});if(this.options.startSpan)  this.setSpan(this.options.startSpan,$R(0,this.values.length>1?this.getRange(0).min():this.value));if(this.options.endSpan) -this.setSpan(this.options.endSpan,$R(this.values.length>1?this.getRange(this.spans.length-1).max():this.value,this.maximum));},setSpan:function(span,range){if(this.isVertical()){span.style.top=this.translateToPx(range.start);span.style.height=this.translateToPx(range.end-range.start+this.range.start);}else{span.style.left=this.translateToPx(range.start);span.style.width=this.translateToPx(range.end-range.start+this.range.start);}},updateStyles:function(){this.handles.each(function(h){Element.removeClassName(h,'selected')});Element.addClassName(this.activeHandle,'selected');},startDrag:function(event){if(Event.isLeftClick(event)){if(!this.disabled){this.active=true;var handle=Event.element(event);var pointer=[Event.pointerX(event),Event.pointerY(event)];if(handle==this.track){var offsets=Position.cumulativeOffset(this.track);this.event=event;this.setValue(this.translateToValue((this.isVertical()?pointer[1]-offsets[1]:pointer[0]-offsets[0])-(this.handleLength/2)));var offsets=Position.cumulativeOffset(this.activeHandle);this.offsetX=(pointer[0]-offsets[0]);this.offsetY=(pointer[1]-offsets[1]);}else{while((this.handles.indexOf(handle)==-1)&&handle.parentNode) +this.setSpan(this.options.endSpan,$R(this.values.length>1?this.getRange(this.spans.length-1).max():this.value,this.maximum));},setSpan:function(span,range){if(this.isVertical()){span.style.top=this.translateToPx(range.start);span.style.height=this.translateToPx(range.end-range.start+this.range.start);}else{span.style.left=this.translateToPx(range.start);span.style.width=this.translateToPx(range.end-range.start+this.range.start);}},updateStyles:function(){this.handles.each(function(h){Element.removeClassName(h,'selected')});Element.addClassName(this.activeHandle,'selected');},startDrag:function(event){if(Event.isLeftClick(event)){if(!this.disabled){this.active=true;var handle=Event.element(event);var pointer=[Event.pointerX(event),Event.pointerY(event)];var track=handle;if(track==this.track){var offsets=Position.cumulativeOffset(this.track);this.event=event;this.setValue(this.translateToValue((this.isVertical()?pointer[1]-offsets[1]:pointer[0]-offsets[0])-(this.handleLength/2)));var offsets=Position.cumulativeOffset(this.activeHandle);this.offsetX=(pointer[0]-offsets[0]);this.offsetY=(pointer[1]-offsets[1]);}else{while((this.handles.indexOf(handle)==-1)&&handle.parentNode)  handle=handle.parentNode;this.activeHandle=handle;this.activeHandleIdx=this.handles.indexOf(this.activeHandle);this.updateStyles();var offsets=Position.cumulativeOffset(this.activeHandle);this.offsetX=(pointer[0]-offsets[0]);this.offsetY=(pointer[1]-offsets[1]);}}  Event.stop(event);}},update:function(event){if(this.active){if(!this.dragging)this.dragging=true;this.draw(event);if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);Event.stop(event);}},draw:function(event){var pointer=[Event.pointerX(event),Event.pointerY(event)];var offsets=Position.cumulativeOffset(this.track);pointer[0]-=this.offsetX+offsets[0];pointer[1]-=this.offsetY+offsets[1];this.event=event;this.setValue(this.translateToValue(this.isVertical()?pointer[1]:pointer[0]));if(this.initialized&&this.options.onSlide)  this.options.onSlide(this.values.length>1?this.values:this.value,this);},endDrag:function(event){if(this.active&&this.dragging){this.finishDrag(event,true);Event.stop(event);} diff --git a/framework/Web/Javascripts/js/clientscripts.php b/framework/Web/Javascripts/js/compressed/clientscripts.php index 3ac3b062..3ac3b062 100644 --- a/framework/Web/Javascripts/js/clientscripts.php +++ b/framework/Web/Javascripts/js/compressed/clientscripts.php diff --git a/framework/Web/Javascripts/js/colorpicker.js b/framework/Web/Javascripts/js/compressed/colorpicker.js index 8429b5e6..8429b5e6 100644 --- a/framework/Web/Javascripts/js/colorpicker.js +++ b/framework/Web/Javascripts/js/compressed/colorpicker.js diff --git a/framework/Web/Javascripts/js/datepicker.js b/framework/Web/Javascripts/js/compressed/datepicker.js index bbf81293..f47e557c 100644 --- a/framework/Web/Javascripts/js/datepicker.js +++ b/framework/Web/Javascripts/js/compressed/datepicker.js @@ -82,7 +82,7 @@ date=new Date();if(typeof(date)=="string"||typeof(date)=="number")  date=new Date(date);return new Date(date.getFullYear(),date.getMonth(),date.getDate(),0,0,0);},setSelectedDate:function(date)  {if(date==null)  return;this.selectedDate=this.newDate(date);this.updateHeader();this.update();if(typeof(this.onChange)=="function") -this.onChange();},getElement:function() +this.onChange(this,date);},getElement:function()  {return this._calDiv;},getSelectedDate:function()  {return this.selectedDate==null?null:this.newDate(this.selectedDate);},setYear:function(year)  {var d=this.newDate(this.selectedDate);d.setFullYear(year);this.setSelectedDate(d);},setMonth:function(month) diff --git a/framework/Web/Javascripts/js/effects.js b/framework/Web/Javascripts/js/compressed/effects.js index 14d659b7..8870d77f 100644 --- a/framework/Web/Javascripts/js/effects.js +++ b/framework/Web/Javascripts/js/compressed/effects.js @@ -13,11 +13,11 @@ Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alp  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){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={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))  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=function(pos){return pos;} -Effect.Transitions.sinoidal=function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;} +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;} @@ -35,13 +35,13 @@ Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queu  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.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:this.options.x*position+this.originalLeft+'px',top: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','%'].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.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')  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=width+'px';if(this.options.scaleY)d.height=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({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.options.endcolor=this.element.getStyle('background-color').parseColor('#ffffff');if(!this.options.restorecolor) @@ -51,7 +51,7 @@ Effect.Appear=function(element){element=$(element);var options=Object.extend({fr  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,{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});}})}});} +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();} diff --git a/framework/Web/Javascripts/js/logger.js b/framework/Web/Javascripts/js/compressed/logger.js index aaef1482..aaef1482 100644 --- a/framework/Web/Javascripts/js/logger.js +++ b/framework/Web/Javascripts/js/compressed/logger.js diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/compressed/prado.js index 1ec69786..1ec69786 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/compressed/prado.js diff --git a/framework/Web/Javascripts/js/rico.js b/framework/Web/Javascripts/js/compressed/rico.js index ff75a9f9..ff75a9f9 100644 --- a/framework/Web/Javascripts/js/rico.js +++ b/framework/Web/Javascripts/js/compressed/rico.js diff --git a/framework/Web/Javascripts/js/validator.js b/framework/Web/Javascripts/js/compressed/validator.js index cecc6881..cecc6881 100644 --- a/framework/Web/Javascripts/js/validator.js +++ b/framework/Web/Javascripts/js/compressed/validator.js diff --git a/framework/Web/Javascripts/js/debug/ajax.js b/framework/Web/Javascripts/js/debug/ajax.js new file mode 100644 index 00000000..9a61cabb --- /dev/null +++ b/framework/Web/Javascripts/js/debug/ajax.js @@ -0,0 +1,3303 @@ +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(responderToAdd) { +    if (!this.include(responderToAdd)) +      this.responders.push(responderToAdd); +  }, +   +  unregister: function(responderToRemove) { +    this.responders = this.responders.without(responderToRemove); +  }, +   +  dispatch: function(callback, request, transport, json) { +    this.each(function(responder) { +      if (responder[callback] && typeof responder[callback] == 'function') { +        try { +          responder[callback].apply(responder, [request, transport, json]); +        } catch (e) {} +      } +    }); +  } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ +  onCreate: function() { +    Ajax.activeRequestCount++; +  }, +   +  onComplete: function() { +    Ajax.activeRequestCount--; +  } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { +  setOptions: function(options) { +    this.options = { +      method:       'post', +      asynchronous: true, +      contentType:  'application/x-www-form-urlencoded', +      parameters:   '' +    } +    Object.extend(this.options, options || {}); +  }, + +  responseIsSuccess: function() { +    return this.transport.status == undefined +        || this.transport.status == 0  +        || (this.transport.status >= 200 && this.transport.status < 300); +  }, + +  responseIsFailure: function() { +    return !this.responseIsSuccess(); +  } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events =  +  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { +  initialize: function(url, options) { +    this.transport = Ajax.getTransport(); +    this.setOptions(options); +    this.request(url); +  }, + +  request: function(url) { +    var parameters = this.options.parameters || ''; +    if (parameters.length > 0) parameters += '&_='; + +    try { +      this.url = url; +      if (this.options.method == 'get' && parameters.length > 0) +        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; +       +      Ajax.Responders.dispatch('onCreate', this, this.transport); +       +      this.transport.open(this.options.method, this.url,  +        this.options.asynchronous); + +      if (this.options.asynchronous) { +        this.transport.onreadystatechange = this.onStateChange.bind(this); +        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); +      } + +      this.setRequestHeaders(); + +      var body = this.options.postBody ? this.options.postBody : parameters; +      this.transport.send(this.options.method == 'post' ? body : null); + +    } catch (e) { +      this.dispatchException(e); +    } +  }, + +  setRequestHeaders: function() { +    var requestHeaders =  +      ['X-Requested-With', 'XMLHttpRequest', +       'X-Prototype-Version', Prototype.Version, +       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*']; + +    if (this.options.method == 'post') { +      requestHeaders.push('Content-type', this.options.contentType); + +      /* Force "Connection: close" for Mozilla browsers to work around +       * a bug where XMLHttpReqeuest sends an incorrect Content-length +       * header. See Mozilla Bugzilla #246651.  +       */ +      if (this.transport.overrideMimeType) +        requestHeaders.push('Connection', 'close'); +    } + +    if (this.options.requestHeaders) +      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + +    for (var i = 0; i < requestHeaders.length; i += 2) +      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); +  }, + +  onStateChange: function() { +    var readyState = this.transport.readyState; +    if (readyState != 1) +      this.respondToReadyState(this.transport.readyState); +  }, +   +  header: function(name) { +    try { +      return this.transport.getResponseHeader(name); +    } catch (e) {} +  }, +   +  evalJSON: function() { +    try { +      return eval('(' + this.header('X-JSON') + ')'); +    } catch (e) {} +  }, +   +  evalResponse: function() { +    try { +      return eval(this.transport.responseText); +    } catch (e) { +      this.dispatchException(e); +    } +  }, + +  respondToReadyState: function(readyState) { +    var event = Ajax.Request.Events[readyState]; +    var transport = this.transport, json = this.evalJSON(); + +    if (event == 'Complete') { +      try { +        (this.options['on' + this.transport.status] +         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] +         || Prototype.emptyFunction)(transport, json); +      } catch (e) { +        this.dispatchException(e); +      } +       +      if ((this.header('Content-type') || '').match(/^text\/javascript/i)) +        this.evalResponse(); +    } +     +    try { +      (this.options['on' + event] || Prototype.emptyFunction)(transport, json); +      Ajax.Responders.dispatch('on' + event, this, transport, json); +    } catch (e) { +      this.dispatchException(e); +    } +     +    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ +    if (event == 'Complete') +      this.transport.onreadystatechange = Prototype.emptyFunction; +  }, +   +  dispatchException: function(exception) { +    (this.options.onException || Prototype.emptyFunction)(this, exception); +    Ajax.Responders.dispatch('onException', this, exception); +  } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { +  initialize: function(container, url, options) { +    this.containers = { +      success: container.success ? $(container.success) : $(container), +      failure: container.failure ? $(container.failure) : +        (container.success ? null : $(container)) +    } + +    this.transport = Ajax.getTransport(); +    this.setOptions(options); + +    var onComplete = this.options.onComplete || Prototype.emptyFunction; +    this.options.onComplete = (function(transport, object) { +      this.updateContent(); +      onComplete(transport, object); +    }).bind(this); + +    this.request(url); +  }, + +  updateContent: function() { +    var receiver = this.responseIsSuccess() ? +      this.containers.success : this.containers.failure; +    var response = this.transport.responseText; +     +    if (!this.options.evalScripts) +      response = response.stripScripts(); + +    if (receiver) { +      if (this.options.insertion) { +        new this.options.insertion(receiver, response); +      } else { +        Element.update(receiver, response); +      } +    } + +    if (this.responseIsSuccess()) { +      if (this.onComplete) +        setTimeout(this.onComplete.bind(this), 10); +    } +  } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { +  initialize: function(container, url, options) { +    this.setOptions(options); +    this.onComplete = this.options.onComplete; + +    this.frequency = (this.options.frequency || 2); +    this.decay = (this.options.decay || 1); +     +    this.updater = {}; +    this.container = container; +    this.url = url; + +    this.start(); +  }, + +  start: function() { +    this.options.onComplete = this.updateComplete.bind(this); +    this.onTimerEvent(); +  }, + +  stop: function() { +    this.updater.onComplete = undefined; +    clearTimeout(this.timer); +    (this.onComplete || Prototype.emptyFunction).apply(this, arguments); +  }, + +  updateComplete: function(request) { +    if (this.options.decay) { +      this.decay = (request.responseText == this.lastText ?  +        this.decay * this.options.decay : 1); + +      this.lastText = request.responseText; +    } +    this.timer = setTimeout(this.onTimerEvent.bind(this),  +      this.decay * this.frequency * 1000); +  }, + +  onTimerEvent: function() { +    this.updater = new Ajax.Updater(this.container, this.url, this.options); +  } +}); + + +/**
 + * Prado AJAX service. The default service provider is JPSpan.
 + */
 +Prado.AJAX = { Service : 'Prototype' };
 +
 +/**
 + * Parse and execute javascript embedded in html.
 + */
 +Prado.AJAX.EvalScript = function(output)
 +{
 +
 +	var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
 +	var scripts  = output.match(match);
 +	if (scripts)
 +	{
 +		match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
 +		setTimeout((function()
 +		{
 +			for (var i = 0; i < scripts.length; i++)
 +				eval(scripts[i].match(match)[1]);
 +		}).bind(this), 50);
 +	}
 +}
 +
 +
 +/**
 + * AJAX service request using Prototype's AJAX request class.
 + */
 +Prado.AJAX.Request = Class.create();
 +Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype,
 +{
 +	/**
 +	 * Evaluate the respond JSON data, override parent implementing.
 +	 * If default eval fails, try parsing the JSON data (slower).
 +	 */
 +	evalJSON: function()
 +	{
 +		try
 +		{
 +			var json = this.transport.getResponseHeader('X-JSON'), object;
 +			object = eval(json);
 +			return object;
 +		}
 +		catch (e)
 +		{
 +			if(isString(json))
 +			{
 +				return Prado.AJAX.JSON.parse(json);
 +			}
 +		}
 +	},
 +
 +	respondToReadyState: function(readyState) {
 +    var event = Ajax.Request.Events[readyState];
 +    var transport = this.transport, json = this.evalJSON();
 +
 +
 +	if(event == 'Complete' && transport.status)
 +    	Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
 +
 +   (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
 +    Ajax.Responders.dispatch('on' + event, this, transport, json);
 +
 +	if (event == 'Complete')
 +      (this.options['on' + this.transport.status]
 +       || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
 +       || Prototype.emptyFunction)(transport, json);
 +
 +
 +    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
 +    if (event == 'Complete')
 +      this.transport.onreadystatechange = Prototype.emptyFunction;
 +  }
 +
 +});
 +
 +Prado.AJAX.Error = function(e, code)
 +{
 +    e.name = 'Prado.AJAX.Error';
 +    e.code = code;
 +    return e;
 +}
 +
 +/**
 + * Post data builder, serialize the data using JSON.
 + */
 +Prado.AJAX.RequestBuilder = Class.create();
 +Prado.AJAX.RequestBuilder.prototype =
 +{
 +	initialize : function()
 +	{
 +		this.body = '';
 +		this.data = [];
 +	},
 +	encode : function(data)
 +	{
 +		return Prado.AJAX.JSON.stringify(data);
 +	},
 +	build : function(data)
 +	{
 +		var sep = '';
 +        for ( var argName in data)
 +		{
 +			if(isFunction(data[argName])) continue;
 +            try
 +			{
 +                this.body += sep + argName + '=';
 +                this.body += encodeURIComponent(this.encode(data[argName]));
 +            } catch (e) {
 +                throw Prado.AJAX.Error(e, 1006);
 +            }
 +            sep = '&';
 +        }
 +    },
 +
 +	getAll : function()
 +	{
 +		this.build(this.data);
 +		return this.body;
 +	}
 +}
 +
 +
 +Prado.AJAX.RemoteObject = function(){};
 +
 +/**
 + * AJAX service request for Prado RemoteObjects
 + */
 +Prado.AJAX.RemoteObject.Request = Class.create();
 +Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.prototype,
 +{
 +	/**
 +	 * Initialize the RemoteObject Request, overrides parent
 +	 * implementation by delaying the request to invokeRemoteObject.
 +	 */
 +	initialize : function(options)
 +	{
 +	    this.transport = Ajax.getTransport();
 +		this.setOptions(options);
 +		this.post = new Prado.AJAX.RequestBuilder();
 +	},
 +
 +	/**
 +	 * Call the remote object,
 +	 * @param string the remote server url
 +	 * @param array additional arguments
 +	 */
 +	invokeRemoteObject : function(url, args)
 +	{
 +		this.initParameters(args);
 +		this.options.postBody = this.post.getAll();
 +		this.request(url);
 +	},
 +
 +	/**
 +	 * Set the additional arguments as post data with key '__parameters'
 +	 */
 +	initParameters : function(args)
 +	{
 +		this.post.data['__parameters'] = [];
 +		for(var i = 0; i<args.length; i++)
 +			this.post.data['__parameters'][i] = args[i];
 +	}
 +});
 +
 +/**
 + * Base proxy class for Prado RemoteObjects via AJAX.
 + * e.g.
 + * <code>
 + *	var TestObject1 = Class.create();
 + *	TestObject1.prototype = Object.extend(new Prado.AJAX.RemoteObject(),
 + *	{
 + * 		initialize : function(handlers, options)
 + *      {
 + *           this.__serverurl = 'http://127.0.0.1/.....';
 + *           this.baseInitialize(handlers, options);
 + *	    }
 + *
 + *		method1 : function()
 + *		{
 + *			return this.__call(this.__serverurl, 'method1', arguments);
 + *		}
 + *	});
 + *</code>
 + * And client usage,
 + * <code>
 + *	var test1 = new TestObject1(); //create new remote object
 + *	test1.method1(); //call the method, no onComplete hook
 + *
 + *  var onComplete = { method1 : function(result){ alert(result) } };
 + *  //create new remote object with onComplete callback
 + *  var test2 = new TestObject1(onComplete);
 + *  test2.method1(); //call it, on success, onComplete's method1 is called.
 + * </code>
 + */
 +Prado.AJAX.RemoteObject.prototype =
 +{
 +	baseInitialize : function(handlers, options)
 +	{
 +		this.__handlers = handlers || {};
 +		this.__service = new Prado.AJAX.RemoteObject.Request(options);
 +	},
 +
 +	__call : function(url, method, args)
 +	{
 +		this.__service.options.onSuccess = this.__onSuccess.bind(this);
 +		this.__callback = method;
 +		return this.__service.invokeRemoteObject(url+"/"+method, args);
 +	},
 +
 +	__onSuccess : function(transport, json)
 +	{
 +		if(this.__handlers[this.__callback])
 +			this.__handlers[this.__callback](json, transport.responseText);
 +	}
 +};
 +
 +/**
 + * Respond to Prado AJAX request exceptions.
 + */
 +Prado.AJAX.Exception =
 +{
 +	/**
 +	 * Server returns 505 exception. Just log it.
 +	 */
 +	"on505" : function(request, transport, e)
 +	{
 +		var msg = 'HTTP '+transport.status+" with response";
 +		Logger.error(msg, transport.responseText);
 +		Logger.exception(e);
 +	},
 +
 +	onComplete : function(request, transport, e)
 +	{
 +		if(transport.status != 505)
 +		{
 +			var msg = 'HTTP '+transport.status+" with response : \n";
 +			msg += transport.responseText + "\n";
 +			msg += "Data : \n"+inspect(e);
 +			Logger.warn(msg);
 +		}
 +	},
 +
 +	format : function(e)
 +	{
 +		var msg = e.type + " with message \""+e.message+"\"";
 +		msg += " in "+e.file+"("+e.line+")\n";
 +		msg += "Stack trace:\n";
 +		var trace = e.trace;
 +		for(var i = 0; i<trace.length; i++)
 +		{
 +			msg += "  #"+i+" "+trace[i].file;
 +			msg += "("+trace[i].line+"): ";
 +			msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n";
 +		}
 +		return msg;
 +	},
 +
 +	logException : function(e)
 +	{
 +		var msg = Prado.AJAX.Exception.format(e);
 +		Logger.error("Server Error "+e.code, msg);
 +	}
 +}
 +
 +//Add HTTP exception respones when logger is enabled.
 +Event.OnLoad(function()
 +{
 +	if(typeof Logger != "undefined")
 +	{
 +		Logger.exception = Prado.AJAX.Exception.logException;
 +		Ajax.Responders.register(Prado.AJAX.Exception);
 +	}
 +});
 +
 +/**
 + * Prado Callback service that provides component intergration,
 + * viewstate (read only), and automatic form data serialization.
 + * Usage: <code>new Prado.AJAX.Callback('MyPage.MyComponentID.raiseCallbackEvent', options)</code>
 + * These classes should be called by the components developers.
 + * For inline callback service, use <t>Prado.Callback(callbackID, params)</t>.
 + */
 +Prado.AJAX.Callback = Class.create();
 +Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(),
 +{
 +
 +	/**
 +	 * Create and request a new Prado callback service.
 +	 * @param string|element the callback ID, must be of the form, <t>ClassName.ComponentID.MethodName</t>
 +	 * @param list options with list key onCallbackReturn, and more.
 +	 *
 +	 */
 +	initialize : function(ID, options)
 +	{
 +		if(!isString(ID) && typeof(ID.id) != "undefined")
 +			ID = ID.id;
 +		if(!isString(ID))
 +			throw new Error('A Control ID must be specified');
 +		this.baseInitialize(this, options);
 +		this.options = options || [];
 +		this.__service.post.data['__ID'] = ID;
 +		this.requestCallback();
 +	},
 +
 +	/**
 +	 * Get form data for components that implements IPostBackHandler.
 +	 */
 +	collectPostData : function()
 +	{
 +		var IDs = Prado.AJAX.Callback.IDs;
 +		this.__service.post.data['__data'] = {};
 +		for(var i = 0; i<IDs.length; i++)
 +		{
 +			var id = IDs[i];
 +			if(id.indexOf("[]") > -1)
 +				this.__service.post.data['__data'][id] =
 +					this.collectArrayPostData(id);
 +			else if(isObject($(id)))
 +				this.__service.post.data['__data'][id] = $F(id);
 +		}
 +	},
 +
 +	collectArrayPostData : function(name)
 +	{
 +		var elements = document.getElementsByName(name);
 +		var data = [];
 +		$A(elements).each(function(el)
 +		{
 +			if($F(el)) data.push($F(el));
 +		});
 +		return data;
 +	},
 +
 +	/**
 +	 * Prepares and calls the AJAX request.
 +	 * Collects the data from components that implements IPostBackHandler
 +	 * and the viewstate as part of the request payload.
 +	 */
 +	requestCallback : function()
 +	{
 +		this.collectPostData();
 +		if(Prado.AJAX.Validate(this.options))
 +			return this.__call(Prado.AJAX.Callback.Server, 'handleCallback', this.options.params);
 +	},
 +
 +	/**
 +	 * On callback request return, call the onSuccess function.
 +	 */
 +	handleCallback : function(result, output)
 +	{
 +		if(typeof(result) != "undefined" && !isNull(result))
 +		{
 +			this.options.onSuccess(result['data'], output);
 +			if(result['actions'])
 +				result.actions.each(Prado.AJAX.Callback.Action.__run);
 +		}
 +	}
 +});
 +
 +/**
 + * Prase and evaluate Callback clien-side actions.
 + */
 +Prado.AJAX.Callback.Action =
 +{
 +	__run : function(command)
 +	{
 +		for(var name in command)
 +		{
 +			//first parameter must be a valid element or begins with '@'
 +			if(command[name][0] && ($(command[name][0]) || command[name][0].indexOf("[]") > -1))
 +			{
 +				name.toFunction().apply(this,command[name]);
 +			}
 +		}
 +	}
 +};
 +
 +
 +/**
 + * Returns false if validation required and validates to false,
 + * returns true otherwise.
 + * @return boolean true if validation passes.
 + */
 +Prado.AJAX.Validate = function(options)
 +{
 +	if(options.CausesValidation)
 +	{
 +		if(options.ValidatorGroup)
 +			return Prado.Validation.ValidateValidatorGroup(options.ValidatorGroup);
 +		else if(options.ValidationGroup)
 +			return Prado.Validation.ValidateValidationGroup(options.ValidationGroup);
 +		else
 +			return Prado.Validation.ValidateNonGroup(options.ValidationForm);
 +	}
 +	else
 +		return true;
 +};
 +
 +
 +//Available callback service
 +Prado.AJAX.Callback.Server = '';
 +
 +//List of IDs that implements IPostBackHandler
 +Prado.AJAX.Callback.IDs = [];
 +
 +/**
 + * Simple AJAX callback interface, suitable for inline javascript.
 + * e.g., <code><a href="..." onclick="Prado.Callback('..', 'Hello');">Click me</a></code>
 + * @param {String} callback ID
 + * @param {Array} parameters to pass to the callback service
 + * @param {Function} on callback success handler method
 + * @param {Object} additional callback options
 + */
 +Prado.Callback = function(ID, params, onSuccess, options)
 +{
 +	var callback =
 +	{
 +		'params' : [params] || [],
 +		'onSuccess' : onSuccess || Prototype.emptyFunction,
 +		'CausesValidation' : true
 +	};
 +
 +	Object.extend(callback, options || {});
 +
 +	new Prado.AJAX.Callback(ID, callback);
 +	return false;
 +} + +/*
 +Copyright (c) 2005 JSON.org
 +
 +Permission is hereby granted, free of charge, to any person obtaining a copy
 +of this software and associated documentation files (the "Software"), to deal
 +in the Software without restriction, including without limitation the rights
 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 +copies of the Software, and to permit persons to whom the Software is
 +furnished to do so, subject to the following conditions:
 +
 +The Software shall be used for Good, not Evil.
 +
 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 +SOFTWARE.
 +*/
 +
 +Array.prototype.______array = '______array';
 +
 +Prado.AJAX.JSON = {
 +    org: 'http://www.JSON.org',
 +    copyright: '(c)2005 JSON.org',
 +    license: 'http://www.crockford.com/JSON/license.html',
 +
 +    stringify: function (arg) {
 +        var c, i, l, s = '', v;
 +
 +        switch (typeof arg) {
 +        case 'object':
 +            if (arg) {
 +                if (arg.______array == '______array') {
 +                    for (i = 0; i < arg.length; ++i) {
 +                        v = this.stringify(arg[i]);
 +                        if (s) {
 +                            s += ',';
 +                        }
 +                        s += v;
 +                    }
 +                    return '[' + s + ']';
 +                } else if (typeof arg.toString != 'undefined') {
 +                    for (i in arg) {
 +                        v = arg[i];
 +                        if (typeof v != 'undefined' && typeof v != 'function') {
 +                            v = this.stringify(v);
 +                            if (s) {
 +                                s += ',';
 +                            }
 +                            s += this.stringify(i) + ':' + v;
 +                        }
 +                    }
 +                    return '{' + s + '}';
 +                }
 +            }
 +            return 'null';
 +        case 'number':
 +            return isFinite(arg) ? String(arg) : 'null';
 +        case 'string':
 +            l = arg.length;
 +            s = '"';
 +            for (i = 0; i < l; i += 1) {
 +                c = arg.charAt(i);
 +                if (c >= ' ') {
 +                    if (c == '\\' || c == '"') {
 +                        s += '\\';
 +                    }
 +                    s += c;
 +                } else {
 +                    switch (c) {
 +                        case '\b':
 +                            s += '\\b';
 +                            break;
 +                        case '\f':
 +                            s += '\\f';
 +                            break;
 +                        case '\n':
 +                            s += '\\n';
 +                            break;
 +                        case '\r':
 +                            s += '\\r';
 +                            break;
 +                        case '\t':
 +                            s += '\\t';
 +                            break;
 +                        default:
 +                            c = c.charCodeAt();
 +                            s += '\\u00' + Math.floor(c / 16).toString(16) +
 +                                (c % 16).toString(16);
 +                    }
 +                }
 +            }
 +            return s + '"';
 +        case 'boolean':
 +            return String(arg);
 +        default:
 +            return 'null';
 +        }
 +    },
 +    parse: function (text) {
 +        var at = 0;
 +        var ch = ' ';
 +
 +        function error(m) {
 +            throw {
 +                name: 'JSONError',
 +                message: m,
 +                at: at - 1,
 +                text: text
 +            };
 +        }
 +
 +        function next() {
 +            ch = text.charAt(at);
 +            at += 1;
 +            return ch;
 +        }
 +
 +        function white() {
 +            while (ch) {
 +                if (ch <= ' ') {
 +                    next();
 +                } else if (ch == '/') {
 +                    switch (next()) {
 +                        case '/':
 +                            while (next() && ch != '\n' && ch != '\r') {}
 +                            break;
 +                        case '*':
 +                            next();
 +                            for (;;) {
 +                                if (ch) {
 +                                    if (ch == '*') {
 +                                        if (next() == '/') {
 +                                            next();
 +                                            break;
 +                                        }
 +                                    } else {
 +                                        next();
 +                                    }
 +                                } else {
 +                                    error("Unterminated comment");
 +                                }
 +                            }
 +                            break;
 +                        default:
 +                            error("Syntax error");
 +                    }
 +                } else {
 +                    break;
 +                }
 +            }
 +        }
 +
 +        function string() {
 +            var i, s = '', t, u;
 +
 +            if (ch == '"') {
 +outer:          while (next()) {
 +                    if (ch == '"') {
 +                        next();
 +                        return s;
 +                    } else if (ch == '\\') {
 +                        switch (next()) {
 +                        case 'b':
 +                            s += '\b';
 +                            break;
 +                        case 'f':
 +                            s += '\f';
 +                            break;
 +                        case 'n':
 +                            s += '\n';
 +                            break;
 +                        case 'r':
 +                            s += '\r';
 +                            break;
 +                        case 't':
 +                            s += '\t';
 +                            break;
 +                        case 'u':
 +                            u = 0;
 +                            for (i = 0; i < 4; i += 1) {
 +                                t = parseInt(next(), 16);
 +                                if (!isFinite(t)) {
 +                                    break outer;
 +                                }
 +                                u = u * 16 + t;
 +                            }
 +                            s += String.fromCharCode(u);
 +                            break;
 +                        default:
 +                            s += ch;
 +                        }
 +                    } else {
 +                        s += ch;
 +                    }
 +                }
 +            }
 +            error("Bad string");
 +        }
 +
 +        function array() {
 +            var a = [];
 +
 +            if (ch == '[') {
 +                next();
 +                white();
 +                if (ch == ']') {
 +                    next();
 +                    return a;
 +                }
 +                while (ch) {
 +                    a.push(value());
 +                    white();
 +                    if (ch == ']') {
 +                        next();
 +                        return a;
 +                    } else if (ch != ',') {
 +                        break;
 +                    }
 +                    next();
 +                    white();
 +                }
 +            }
 +            error("Bad array");
 +        }
 +
 +        function object() {
 +            var k, o = {};
 +
 +            if (ch == '{') {
 +                next();
 +                white();
 +                if (ch == '}') {
 +                    next();
 +                    return o;
 +                }
 +                while (ch) {
 +                    k = string();
 +                    white();
 +                    if (ch != ':') {
 +                        break;
 +                    }
 +                    next();
 +                    o[k] = value();
 +                    white();
 +                    if (ch == '}') {
 +                        next();
 +                        return o;
 +                    } else if (ch != ',') {
 +                        break;
 +                    }
 +                    next();
 +                    white();
 +                }
 +            }
 +            error("Bad object");
 +        }
 +
 +        function number() {
 +            var n = '', v;
 +            if (ch == '-') {
 +                n = '-';
 +                next();
 +            }
 +            while (ch >= '0' && ch <= '9') {
 +                n += ch;
 +                next();
 +            }
 +            if (ch == '.') {
 +                n += '.';
 +                while (next() && ch >= '0' && ch <= '9') {
 +                    n += ch;
 +                }
 +            }
 +            if (ch == 'e' || ch == 'E') {
 +                n += 'e';
 +                next();
 +                if (ch == '-' || ch == '+') {
 +                    n += ch;
 +                    next();
 +                }
 +                while (ch >= '0' && ch <= '9') {
 +                    n += ch;
 +                    next();
 +                }
 +            }
 +            v = +n;
 +            if (!isFinite(v)) {
 +                ////error("Bad number");
 +            } else {
 +                return v;
 +            }
 +        }
 +
 +        function word() {
 +            switch (ch) {
 +                case 't':
 +                    if (next() == 'r' && next() == 'u' && next() == 'e') {
 +                        next();
 +                        return true;
 +                    }
 +                    break;
 +                case 'f':
 +                    if (next() == 'a' && next() == 'l' && next() == 's' &&
 +                            next() == 'e') {
 +                        next();
 +                        return false;
 +                    }
 +                    break;
 +                case 'n':
 +                    if (next() == 'u' && next() == 'l' && next() == 'l') {
 +                        next();
 +                        return null;
 +                    }
 +                    break;
 +            }
 +            error("Syntax error");
 +        }
 +
 +        function value() {
 +            white();
 +            switch (ch) {
 +                case '{':
 +                    return object();
 +                case '[':
 +                    return array();
 +                case '"':
 +                    return string();
 +                case '-':
 +                    return number();
 +                default:
 +                    return ch >= '0' && ch <= '9' ? number() : word();
 +            }
 +        }
 +
 +        return value();
 +    }
 +}; + +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +//           (c) 2005 Jon Tirsen (http://www.tirsen.com) +// Contributors: +//  Richard Livsey +//  Rahul Bhargava +//  Rob Wills +//  +// See scriptaculous.js for full license. + +// Autocompleter.Base handles all the autocompletion functionality  +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least,  +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method  +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most  +// useful when one of the tokens is \n (a newline), as it  +// allows smart autocompletion after linebreaks. + +if(typeof Effect == 'undefined') +  throw("controls.js requires including script.aculo.us' effects.js library"); + +var Autocompleter = {} +Autocompleter.Base = function() {}; +Autocompleter.Base.prototype = { +  baseInitialize: function(element, update, options) { +    this.element     = $(element);  +    this.update      = $(update);   +    this.hasFocus    = false;  +    this.changed     = false;  +    this.active      = false;  +    this.index       = 0;      +    this.entryCount  = 0; + +    if (this.setOptions) +      this.setOptions(options); +    else +      this.options = options || {}; + +    this.options.paramName    = this.options.paramName || this.element.name; +    this.options.tokens       = this.options.tokens || []; +    this.options.frequency    = this.options.frequency || 0.4; +    this.options.minChars     = this.options.minChars || 1; +    this.options.onShow       = this.options.onShow ||  +    function(element, update){  +      if(!update.style.position || update.style.position=='absolute') { +        update.style.position = 'absolute'; +        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight}); +      } +      Effect.Appear(update,{duration:0.15}); +    }; +    this.options.onHide = this.options.onHide ||  +    function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + +    if (typeof(this.options.tokens) == 'string')  +      this.options.tokens = new Array(this.options.tokens); + +    this.observer = null; +     +    this.element.setAttribute('autocomplete','off'); + +    Element.hide(this.update); + +    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); +    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); +  }, + +  show: function() { +    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); +    if(!this.iefix &&  +      (navigator.appVersion.indexOf('MSIE')>0) && +      (navigator.userAgent.indexOf('Opera')<0) && +      (Element.getStyle(this.update, 'position')=='absolute')) { +      new Insertion.After(this.update,  +       '<iframe id="' + this.update.id + '_iefix" '+ +       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + +       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); +      this.iefix = $(this.update.id+'_iefix'); +    } +    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); +  }, +   +  fixIEOverlapping: function() { +    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); +    this.iefix.style.zIndex = 1; +    this.update.style.zIndex = 2; +    Element.show(this.iefix); +  }, + +  hide: function() { +    this.stopIndicator(); +    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); +    if(this.iefix) Element.hide(this.iefix); +  }, + +  startIndicator: function() { +    if(this.options.indicator) Element.show(this.options.indicator); +  }, + +  stopIndicator: function() { +    if(this.options.indicator) Element.hide(this.options.indicator); +  }, + +  onKeyPress: function(event) { +    if(this.active) +      switch(event.keyCode) { +       case Event.KEY_TAB: +       case Event.KEY_RETURN: +         this.selectEntry(); +         Event.stop(event); +       case Event.KEY_ESC: +         this.hide(); +         this.active = false; +         Event.stop(event); +         return; +       case Event.KEY_LEFT: +       case Event.KEY_RIGHT: +         return; +       case Event.KEY_UP: +         this.markPrevious(); +         this.render(); +         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); +         return; +       case Event.KEY_DOWN: +         this.markNext(); +         this.render(); +         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); +         return; +      } +     else  +       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||  +         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; + +    this.changed = true; +    this.hasFocus = true; + +    if(this.observer) clearTimeout(this.observer); +      this.observer =  +        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); +  }, + +  activate: function() { +    this.changed = false; +    this.hasFocus = true; +    this.getUpdatedChoices(); +  }, + +  onHover: function(event) { +    var element = Event.findElement(event, 'LI'); +    if(this.index != element.autocompleteIndex)  +    { +        this.index = element.autocompleteIndex; +        this.render(); +    } +    Event.stop(event); +  }, +   +  onClick: function(event) { +    var element = Event.findElement(event, 'LI'); +    this.index = element.autocompleteIndex; +    this.selectEntry(); +    this.hide(); +  }, +   +  onBlur: function(event) { +    // needed to make click events working +    setTimeout(this.hide.bind(this), 250); +    this.hasFocus = false; +    this.active = false;      +  },  +   +  render: function() { +    if(this.entryCount > 0) { +      for (var i = 0; i < this.entryCount; i++) +        this.index==i ?  +          Element.addClassName(this.getEntry(i),"selected") :  +          Element.removeClassName(this.getEntry(i),"selected"); +         +      if(this.hasFocus) {  +        this.show(); +        this.active = true; +      } +    } else { +      this.active = false; +      this.hide(); +    } +  }, +   +  markPrevious: function() { +    if(this.index > 0) this.index-- +      else this.index = this.entryCount-1; +    this.getEntry(this.index).scrollIntoView(true); +  }, +   +  markNext: function() { +    if(this.index < this.entryCount-1) this.index++ +      else this.index = 0; +    this.getEntry(this.index).scrollIntoView(false); +  }, +   +  getEntry: function(index) { +    return this.update.firstChild.childNodes[index]; +  }, +   +  getCurrentEntry: function() { +    return this.getEntry(this.index); +  }, +   +  selectEntry: function() { +    this.active = false; +    this.updateElement(this.getCurrentEntry()); +  }, + +  updateElement: function(selectedElement) { +    if (this.options.updateElement) { +      this.options.updateElement(selectedElement); +      return; +    } +    var value = ''; +    if (this.options.select) { +      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; +      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); +    } else +      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); +     +    var lastTokenPos = this.findLastToken(); +    if (lastTokenPos != -1) { +      var newValue = this.element.value.substr(0, lastTokenPos + 1); +      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); +      if (whitespace) +        newValue += whitespace[0]; +      this.element.value = newValue + value; +    } else { +      this.element.value = value; +    } +    this.element.focus(); +     +    if (this.options.afterUpdateElement) +      this.options.afterUpdateElement(this.element, selectedElement); +  }, + +  updateChoices: function(choices) { +    if(!this.changed && this.hasFocus) { +      this.update.innerHTML = choices; +      Element.cleanWhitespace(this.update); +      Element.cleanWhitespace(this.update.firstChild); + +      if(this.update.firstChild && this.update.firstChild.childNodes) { +        this.entryCount =  +          this.update.firstChild.childNodes.length; +        for (var i = 0; i < this.entryCount; i++) { +          var entry = this.getEntry(i); +          entry.autocompleteIndex = i; +          this.addObservers(entry); +        } +      } else {  +        this.entryCount = 0; +      } + +      this.stopIndicator(); + +      this.index = 0; +      this.render(); +    } +  }, + +  addObservers: function(element) { +    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); +    Event.observe(element, "click", this.onClick.bindAsEventListener(this)); +  }, + +  onObserverEvent: function() { +    this.changed = false;    +    if(this.getToken().length>=this.options.minChars) { +      this.startIndicator(); +      this.getUpdatedChoices(); +    } else { +      this.active = false; +      this.hide(); +    } +  }, + +  getToken: function() { +    var tokenPos = this.findLastToken(); +    if (tokenPos != -1) +      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); +    else +      var ret = this.element.value; + +    return /\n/.test(ret) ? '' : ret; +  }, + +  findLastToken: function() { +    var lastTokenPos = -1; + +    for (var i=0; i<this.options.tokens.length; i++) { +      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]); +      if (thisTokenPos > lastTokenPos) +        lastTokenPos = thisTokenPos; +    } +    return lastTokenPos; +  } +} + +Ajax.Autocompleter = Class.create(); +Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { +  initialize: function(element, update, url, options) { +    this.baseInitialize(element, update, options); +    this.options.asynchronous  = true; +    this.options.onComplete    = this.onComplete.bind(this); +    this.options.defaultParams = this.options.parameters || null; +    this.url                   = url; +  }, + +  getUpdatedChoices: function() { +    entry = encodeURIComponent(this.options.paramName) + '=' +  +      encodeURIComponent(this.getToken()); + +    this.options.parameters = this.options.callback ? +      this.options.callback(this.element, entry) : entry; + +    if(this.options.defaultParams)  +      this.options.parameters += '&' + this.options.defaultParams; + +    new Ajax.Request(this.url, this.options); +  }, + +  onComplete: function(request) { +    this.updateChoices(request.responseText); +  } + +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +//                    text only at the beginning of strings in the  +//                    autocomplete array. Defaults to true, which will +//                    match text at the beginning of any *word* in the +//                    strings in the autocomplete array. If you want to +//                    search anywhere in the string, additionally set +//                    the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +//                   a partial match (unlike minChars, which defines +//                   how many characters are required to do any match +//                   at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +//                 Defaults to true. +// +// It's possible to pass in a custom function as the 'selector'  +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(); +Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { +  initialize: function(element, update, array, options) { +    this.baseInitialize(element, update, options); +    this.options.array = array; +  }, + +  getUpdatedChoices: function() { +    this.updateChoices(this.options.selector(this)); +  }, + +  setOptions: function(options) { +    this.options = Object.extend({ +      choices: 10, +      partialSearch: true, +      partialChars: 2, +      ignoreCase: true, +      fullSearch: false, +      selector: function(instance) { +        var ret       = []; // Beginning matches +        var partial   = []; // Inside matches +        var entry     = instance.getToken(); +        var count     = 0; + +        for (var i = 0; i < instance.options.array.length &&   +          ret.length < instance.options.choices ; i++) {  + +          var elem = instance.options.array[i]; +          var foundPos = instance.options.ignoreCase ?  +            elem.toLowerCase().indexOf(entry.toLowerCase()) :  +            elem.indexOf(entry); + +          while (foundPos != -1) { +            if (foundPos == 0 && elem.length != entry.length) {  +              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +  +                elem.substr(entry.length) + "</li>"); +              break; +            } else if (entry.length >= instance.options.partialChars &&  +              instance.options.partialSearch && foundPos != -1) { +              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { +                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + +                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( +                  foundPos + entry.length) + "</li>"); +                break; +              } +            } + +            foundPos = instance.options.ignoreCase ?  +              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :  +              elem.indexOf(entry, foundPos + 1); + +          } +        } +        if (partial.length) +          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) +        return "<ul>" + ret.join('') + "</ul>"; +      } +    }, options || {}); +  } +}); + +// AJAX in-place editor +// +// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { +  setTimeout(function() { +    Field.activate(field); +  }, 1); +} + +Ajax.InPlaceEditor = Class.create(); +Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; +Ajax.InPlaceEditor.prototype = { +  initialize: function(element, url, options) { +    this.url = url; +    this.element = $(element); + +    this.options = Object.extend({ +      okButton: true, +      okText: "ok", +      cancelLink: true, +      cancelText: "cancel", +      savingText: "Saving...", +      clickToEditText: "Click to edit", +      okText: "ok", +      rows: 1, +      onComplete: function(transport, element) { +        new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); +      }, +      onFailure: function(transport) { +        alert("Error communicating with the server: " + transport.responseText.stripTags()); +      }, +      callback: function(form) { +        return Form.serialize(form); +      }, +      handleLineBreaks: true, +      loadingText: 'Loading...', +      savingClassName: 'inplaceeditor-saving', +      loadingClassName: 'inplaceeditor-loading', +      formClassName: 'inplaceeditor-form', +      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, +      highlightendcolor: "#FFFFFF", +      externalControl: null, +      submitOnBlur: false, +      ajaxOptions: {}, +      evalScripts: false +    }, options || {}); + +    if(!this.options.formId && this.element.id) { +      this.options.formId = this.element.id + "-inplaceeditor"; +      if ($(this.options.formId)) { +        // there's already a form with that name, don't specify an id +        this.options.formId = null; +      } +    } +     +    if (this.options.externalControl) { +      this.options.externalControl = $(this.options.externalControl); +    } +     +    this.originalBackground = Element.getStyle(this.element, 'background-color'); +    if (!this.originalBackground) { +      this.originalBackground = "transparent"; +    } +     +    this.element.title = this.options.clickToEditText; +     +    this.onclickListener = this.enterEditMode.bindAsEventListener(this); +    this.mouseoverListener = this.enterHover.bindAsEventListener(this); +    this.mouseoutListener = this.leaveHover.bindAsEventListener(this); +    Event.observe(this.element, 'click', this.onclickListener); +    Event.observe(this.element, 'mouseover', this.mouseoverListener); +    Event.observe(this.element, 'mouseout', this.mouseoutListener); +    if (this.options.externalControl) { +      Event.observe(this.options.externalControl, 'click', this.onclickListener); +      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); +      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); +    } +  }, +  enterEditMode: function(evt) { +    if (this.saving) return; +    if (this.editing) return; +    this.editing = true; +    this.onEnterEditMode(); +    if (this.options.externalControl) { +      Element.hide(this.options.externalControl); +    } +    Element.hide(this.element); +    this.createForm(); +    this.element.parentNode.insertBefore(this.form, this.element); +    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); +    // stop the event to avoid a page refresh in Safari +    if (evt) { +      Event.stop(evt); +    } +    return false; +  }, +  createForm: function() { +    this.form = document.createElement("form"); +    this.form.id = this.options.formId; +    Element.addClassName(this.form, this.options.formClassName) +    this.form.onsubmit = this.onSubmit.bind(this); + +    this.createEditField(); + +    if (this.options.textarea) { +      var br = document.createElement("br"); +      this.form.appendChild(br); +    } + +    if (this.options.okButton) { +      okButton = document.createElement("input"); +      okButton.type = "submit"; +      okButton.value = this.options.okText; +      okButton.className = 'editor_ok_button'; +      this.form.appendChild(okButton); +    } + +    if (this.options.cancelLink) { +      cancelLink = document.createElement("a"); +      cancelLink.href = "#"; +      cancelLink.appendChild(document.createTextNode(this.options.cancelText)); +      cancelLink.onclick = this.onclickCancel.bind(this); +      cancelLink.className = 'editor_cancel';       +      this.form.appendChild(cancelLink); +    } +  }, +  hasHTMLLineBreaks: function(string) { +    if (!this.options.handleLineBreaks) return false; +    return string.match(/<br/i) || string.match(/<p>/i); +  }, +  convertHTMLLineBreaks: function(string) { +    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, ""); +  }, +  createEditField: function() { +    var text; +    if(this.options.loadTextURL) { +      text = this.options.loadingText; +    } else { +      text = this.getText(); +    } + +    var obj = this; +     +    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { +      this.options.textarea = false; +      var textField = document.createElement("input"); +      textField.obj = this; +      textField.type = "text"; +      textField.name = "value"; +      textField.value = text; +      textField.style.backgroundColor = this.options.highlightcolor; +      textField.className = 'editor_field'; +      var size = this.options.size || this.options.cols || 0; +      if (size != 0) textField.size = size; +      if (this.options.submitOnBlur) +        textField.onblur = this.onSubmit.bind(this); +      this.editField = textField; +    } else { +      this.options.textarea = true; +      var textArea = document.createElement("textarea"); +      textArea.obj = this; +      textArea.name = "value"; +      textArea.value = this.convertHTMLLineBreaks(text); +      textArea.rows = this.options.rows; +      textArea.cols = this.options.cols || 40; +      textArea.className = 'editor_field';       +      if (this.options.submitOnBlur) +        textArea.onblur = this.onSubmit.bind(this); +      this.editField = textArea; +    } +     +    if(this.options.loadTextURL) { +      this.loadExternalText(); +    } +    this.form.appendChild(this.editField); +  }, +  getText: function() { +    return this.element.innerHTML; +  }, +  loadExternalText: function() { +    Element.addClassName(this.form, this.options.loadingClassName); +    this.editField.disabled = true; +    new Ajax.Request( +      this.options.loadTextURL, +      Object.extend({ +        asynchronous: true, +        onComplete: this.onLoadedExternalText.bind(this) +      }, this.options.ajaxOptions) +    ); +  }, +  onLoadedExternalText: function(transport) { +    Element.removeClassName(this.form, this.options.loadingClassName); +    this.editField.disabled = false; +    this.editField.value = transport.responseText.stripTags(); +    Field.scrollFreeActivate(this.editField); +  }, +  onclickCancel: function() { +    this.onComplete(); +    this.leaveEditMode(); +    return false; +  }, +  onFailure: function(transport) { +    this.options.onFailure(transport); +    if (this.oldInnerHTML) { +      this.element.innerHTML = this.oldInnerHTML; +      this.oldInnerHTML = null; +    } +    return false; +  }, +  onSubmit: function() { +    // onLoading resets these so we need to save them away for the Ajax call +    var form = this.form; +    var value = this.editField.value; +     +    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... +    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... +    // to be displayed indefinitely +    this.onLoading(); +     +    if (this.options.evalScripts) { +      new Ajax.Request( +        this.url, Object.extend({ +          parameters: this.options.callback(form, value), +          onComplete: this.onComplete.bind(this), +          onFailure: this.onFailure.bind(this), +          asynchronous:true,  +          evalScripts:true +        }, this.options.ajaxOptions)); +    } else  { +      new Ajax.Updater( +        { success: this.element, +          // don't update on failure (this could be an option) +          failure: null },  +        this.url, Object.extend({ +          parameters: this.options.callback(form, value), +          onComplete: this.onComplete.bind(this), +          onFailure: this.onFailure.bind(this) +        }, this.options.ajaxOptions)); +    } +    // stop the event to avoid a page refresh in Safari +    if (arguments.length > 1) { +      Event.stop(arguments[0]); +    } +    return false; +  }, +  onLoading: function() { +    this.saving = true; +    this.removeForm(); +    this.leaveHover(); +    this.showSaving(); +  }, +  showSaving: function() { +    this.oldInnerHTML = this.element.innerHTML; +    this.element.innerHTML = this.options.savingText; +    Element.addClassName(this.element, this.options.savingClassName); +    this.element.style.backgroundColor = this.originalBackground; +    Element.show(this.element); +  }, +  removeForm: function() { +    if(this.form) { +      if (this.form.parentNode) Element.remove(this.form); +      this.form = null; +    } +  }, +  enterHover: function() { +    if (this.saving) return; +    this.element.style.backgroundColor = this.options.highlightcolor; +    if (this.effect) { +      this.effect.cancel(); +    } +    Element.addClassName(this.element, this.options.hoverClassName) +  }, +  leaveHover: function() { +    if (this.options.backgroundColor) { +      this.element.style.backgroundColor = this.oldBackground; +    } +    Element.removeClassName(this.element, this.options.hoverClassName) +    if (this.saving) return; +    this.effect = new Effect.Highlight(this.element, { +      startcolor: this.options.highlightcolor, +      endcolor: this.options.highlightendcolor, +      restorecolor: this.originalBackground +    }); +  }, +  leaveEditMode: function() { +    Element.removeClassName(this.element, this.options.savingClassName); +    this.removeForm(); +    this.leaveHover(); +    this.element.style.backgroundColor = this.originalBackground; +    Element.show(this.element); +    if (this.options.externalControl) { +      Element.show(this.options.externalControl); +    } +    this.editing = false; +    this.saving = false; +    this.oldInnerHTML = null; +    this.onLeaveEditMode(); +  }, +  onComplete: function(transport) { +    this.leaveEditMode(); +    this.options.onComplete.bind(this)(transport, this.element); +  }, +  onEnterEditMode: function() {}, +  onLeaveEditMode: function() {}, +  dispose: function() { +    if (this.oldInnerHTML) { +      this.element.innerHTML = this.oldInnerHTML; +    } +    this.leaveEditMode(); +    Event.stopObserving(this.element, 'click', this.onclickListener); +    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); +    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); +    if (this.options.externalControl) { +      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); +      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); +      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); +    } +  } +}; + +Ajax.InPlaceCollectionEditor = Class.create(); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, { +  createEditField: function() { +    if (!this.cached_selectTag) { +      var selectTag = document.createElement("select"); +      var collection = this.options.collection || []; +      var optionTag; +      collection.each(function(e,i) { +        optionTag = document.createElement("option"); +        optionTag.value = (e instanceof Array) ? e[0] : e; +        if(this.options.value==optionTag.value) optionTag.selected = true; +        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); +        selectTag.appendChild(optionTag); +      }.bind(this)); +      this.cached_selectTag = selectTag; +    } + +    this.editField = this.cached_selectTag; +    if(this.options.loadTextURL) this.loadExternalText(); +    this.form.appendChild(this.editField); +    this.options.callback = function(form, value) { +      return "value=" + encodeURIComponent(value); +    } +  } +}); + +// Delayed observer, like Form.Element.Observer,  +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create(); +Form.Element.DelayedObserver.prototype = { +  initialize: function(element, delay, callback) { +    this.delay     = delay || 0.5; +    this.element   = $(element); +    this.callback  = callback; +    this.timer     = null; +    this.lastValue = $F(this.element);  +    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); +  }, +  delayedListener: function(event) { +    if(this.lastValue == $F(this.element)) return; +    if(this.timer) clearTimeout(this.timer); +    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); +    this.lastValue = $F(this.element); +  }, +  onTimerEvent: function() { +    this.timer = null; +    this.callback(this.element, $F(this.element)); +  } +}; + + +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) +//  +// See scriptaculous.js for full license. + +/*--------------------------------------------------------------------------*/ + +if(typeof Effect == 'undefined') +  throw("dragdrop.js requires including script.aculo.us' effects.js library"); + +var Droppables = { +  drops: [], + +  remove: function(element) { +    this.drops = this.drops.reject(function(d) { return d.element==$(element) }); +  }, + +  add: function(element) { +    element = $(element); +    var options = Object.extend({ +      greedy:     true, +      hoverclass: null, +      tree:       false +    }, arguments[1] || {}); + +    // cache containers +    if(options.containment) { +      options._containers = []; +      var containment = options.containment; +      if((typeof containment == 'object') &&  +        (containment.constructor == Array)) { +        containment.each( function(c) { options._containers.push($(c)) }); +      } else { +        options._containers.push($(containment)); +      } +    } +     +    if(options.accept) options.accept = [options.accept].flatten(); + +    Element.makePositioned(element); // fix IE +    options.element = element; + +    this.drops.push(options); +  }, +   +  findDeepestChild: function(drops) { +    deepest = drops[0]; +       +    for (i = 1; i < drops.length; ++i) +      if (Element.isParent(drops[i].element, deepest.element)) +        deepest = drops[i]; +     +    return deepest; +  }, + +  isContained: function(element, drop) { +    var containmentNode; +    if(drop.tree) { +      containmentNode = element.treeNode;  +    } else { +      containmentNode = element.parentNode; +    } +    return drop._containers.detect(function(c) { return containmentNode == c }); +  }, +   +  isAffected: function(point, element, drop) { +    return ( +      (drop.element!=element) && +      ((!drop._containers) || +        this.isContained(element, drop)) && +      ((!drop.accept) || +        (Element.classNames(element).detect(  +          function(v) { return drop.accept.include(v) } ) )) && +      Position.within(drop.element, point[0], point[1]) ); +  }, + +  deactivate: function(drop) { +    if(drop.hoverclass) +      Element.removeClassName(drop.element, drop.hoverclass); +    this.last_active = null; +  }, + +  activate: function(drop) { +    if(drop.hoverclass) +      Element.addClassName(drop.element, drop.hoverclass); +    this.last_active = drop; +  }, + +  show: function(point, element) { +    if(!this.drops.length) return; +    var affected = []; +     +    if(this.last_active) this.deactivate(this.last_active); +    this.drops.each( function(drop) { +      if(Droppables.isAffected(point, element, drop)) +        affected.push(drop); +    }); +         +    if(affected.length>0) { +      drop = Droppables.findDeepestChild(affected); +      Position.within(drop.element, point[0], point[1]); +      if(drop.onHover) +        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); +       +      Droppables.activate(drop); +    } +  }, + +  fire: function(event, element) { +    if(!this.last_active) return; +    Position.prepare(); + +    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) +      if (this.last_active.onDrop)  +        this.last_active.onDrop(element, this.last_active.element, event); +  }, + +  reset: function() { +    if(this.last_active) +      this.deactivate(this.last_active); +  } +} + +var Draggables = { +  drags: [], +  observers: [], +   +  register: function(draggable) { +    if(this.drags.length == 0) { +      this.eventMouseUp   = this.endDrag.bindAsEventListener(this); +      this.eventMouseMove = this.updateDrag.bindAsEventListener(this); +      this.eventKeypress  = this.keyPress.bindAsEventListener(this); +       +      Event.observe(document, "mouseup", this.eventMouseUp); +      Event.observe(document, "mousemove", this.eventMouseMove); +      Event.observe(document, "keypress", this.eventKeypress); +    } +    this.drags.push(draggable); +  }, +   +  unregister: function(draggable) { +    this.drags = this.drags.reject(function(d) { return d==draggable }); +    if(this.drags.length == 0) { +      Event.stopObserving(document, "mouseup", this.eventMouseUp); +      Event.stopObserving(document, "mousemove", this.eventMouseMove); +      Event.stopObserving(document, "keypress", this.eventKeypress); +    } +  }, +   +  activate: function(draggable) { +    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari +    this.activeDraggable = draggable; +  }, +   +  deactivate: function() { +    this.activeDraggable = null; +  }, +   +  updateDrag: function(event) { +    if(!this.activeDraggable) return; +    var pointer = [Event.pointerX(event), Event.pointerY(event)]; +    // Mozilla-based browsers fire successive mousemove events with +    // the same coordinates, prevent needless redrawing (moz bug?) +    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; +    this._lastPointer = pointer; +    this.activeDraggable.updateDrag(event, pointer); +  }, +   +  endDrag: function(event) { +    if(!this.activeDraggable) return; +    this._lastPointer = null; +    this.activeDraggable.endDrag(event); +    this.activeDraggable = null; +  }, +   +  keyPress: function(event) { +    if(this.activeDraggable) +      this.activeDraggable.keyPress(event); +  }, +   +  addObserver: function(observer) { +    this.observers.push(observer); +    this._cacheObserverCallbacks(); +  }, +   +  removeObserver: function(element) {  // element instead of observer fixes mem leaks +    this.observers = this.observers.reject( function(o) { return o.element==element }); +    this._cacheObserverCallbacks(); +  }, +   +  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag' +    if(this[eventName+'Count'] > 0) +      this.observers.each( function(o) { +        if(o[eventName]) o[eventName](eventName, draggable, event); +      }); +  }, +   +  _cacheObserverCallbacks: function() { +    ['onStart','onEnd','onDrag'].each( function(eventName) { +      Draggables[eventName+'Count'] = Draggables.observers.select( +        function(o) { return o[eventName]; } +      ).length; +    }); +  } +} + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create(); +Draggable._revertCache = {}; +Draggable._dragging    = {}; + +Draggable.prototype = { +  initialize: function(element) { +    var options = Object.extend({ +      handle: false, +      starteffect: function(element) { +        element._opacity = Element.getOpacity(element); +        Draggable._dragging[element] = true; +        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});  +      }, +      reverteffect: function(element, top_offset, left_offset) { +        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; +        Draggable._revertCache[element] = +          new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, +            queue: {scope:'_draggable', position:'end'} +          }); +      }, +      endeffect: function(element) { +        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; +        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,  +          queue: {scope:'_draggable', position:'end'}, +          afterFinish: function(){ Draggable._dragging[element] = false } +        });  +      }, +      zindex: 1000, +      revert: false, +      scroll: false, +      scrollSensitivity: 20, +      scrollSpeed: 15, +      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] } +    }, arguments[1] || {}); + +    this.element = $(element); +     +    if(options.handle && (typeof options.handle == 'string')) { +      var h = Element.childrenWithClassName(this.element, options.handle, true); +      if(h.length>0) this.handle = h[0]; +    } +    if(!this.handle) this.handle = $(options.handle); +    if(!this.handle) this.handle = this.element; +     +    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) +      options.scroll = $(options.scroll); + +    Element.makePositioned(this.element); // fix IE     + +    this.delta    = this.currentDelta(); +    this.options  = options; +    this.dragging = false;    + +    this.eventMouseDown = this.initDrag.bindAsEventListener(this); +    Event.observe(this.handle, "mousedown", this.eventMouseDown); +     +    Draggables.register(this); +  }, +   +  destroy: function() { +    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); +    Draggables.unregister(this); +  }, +   +  currentDelta: function() { +    return([ +      parseInt(Element.getStyle(this.element,'left') || '0'), +      parseInt(Element.getStyle(this.element,'top') || '0')]); +  }, +   +  initDrag: function(event) { +    if(typeof Draggable._dragging[this.element] != undefined && +      Draggable._dragging[this.element]) return; +    if(Event.isLeftClick(event)) {     +      // abort on form elements, fixes a Firefox issue +      var src = Event.element(event); +      if(src.tagName && ( +        src.tagName=='INPUT' || +        src.tagName=='SELECT' || +        src.tagName=='OPTION' || +        src.tagName=='BUTTON' || +        src.tagName=='TEXTAREA')) return; +         +      if(Draggable._revertCache[this.element]) { +        Draggable._revertCache[this.element].cancel(); +        Draggable._revertCache[this.element] = null; +      } +       +      var pointer = [Event.pointerX(event), Event.pointerY(event)]; +      var pos     = Position.cumulativeOffset(this.element); +      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); +       +      Draggables.activate(this); +      Event.stop(event); +    } +  }, +   +  startDrag: function(event) { +    this.dragging = true; +     +    if(this.options.zindex) { +      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); +      this.element.style.zIndex = this.options.zindex; +    } +     +    if(this.options.ghosting) { +      this._clone = this.element.cloneNode(true); +      Position.absolutize(this.element); +      this.element.parentNode.insertBefore(this._clone, this.element); +    } +     +    if(this.options.scroll) { +      if (this.options.scroll == window) { +        var where = this._getWindowScroll(this.options.scroll); +        this.originalScrollLeft = where.left; +        this.originalScrollTop = where.top; +      } else { +        this.originalScrollLeft = this.options.scroll.scrollLeft; +        this.originalScrollTop = this.options.scroll.scrollTop; +      } +    } +     +    Draggables.notify('onStart', this, event); +    if(this.options.starteffect) this.options.starteffect(this.element); +  }, +   +  updateDrag: function(event, pointer) { +    if(!this.dragging) this.startDrag(event); +    Position.prepare(); +    Droppables.show(pointer, this.element); +    Draggables.notify('onDrag', this, event); +    this.draw(pointer); +    if(this.options.change) this.options.change(this); +     +    if(this.options.scroll) { +      this.stopScrolling(); +       +      var p; +      if (this.options.scroll == window) { +        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } +      } else { +        p = Position.page(this.options.scroll); +        p[0] += this.options.scroll.scrollLeft; +        p[1] += this.options.scroll.scrollTop; +        p.push(p[0]+this.options.scroll.offsetWidth); +        p.push(p[1]+this.options.scroll.offsetHeight); +      } +      var speed = [0,0]; +      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); +      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); +      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); +      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); +      this.startScrolling(speed); +    } +     +    // fix AppleWebKit rendering +    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +     +    Event.stop(event); +  }, +   +  finishDrag: function(event, success) { +    this.dragging = false; + +    if(this.options.ghosting) { +      Position.relativize(this.element); +      Element.remove(this._clone); +      this._clone = null; +    } + +    if(success) Droppables.fire(event, this.element); +    Draggables.notify('onEnd', this, event); + +    var revert = this.options.revert; +    if(revert && typeof revert == 'function') revert = revert(this.element); +     +    var d = this.currentDelta(); +    if(revert && this.options.reverteffect) { +      this.options.reverteffect(this.element,  +        d[1]-this.delta[1], d[0]-this.delta[0]); +    } else { +      this.delta = d; +    } + +    if(this.options.zindex) +      this.element.style.zIndex = this.originalZ; + +    if(this.options.endeffect)  +      this.options.endeffect(this.element); + +    Draggables.deactivate(this); +    Droppables.reset(); +  }, +   +  keyPress: function(event) { +    if(event.keyCode!=Event.KEY_ESC) return; +    this.finishDrag(event, false); +    Event.stop(event); +  }, +   +  endDrag: function(event) { +    if(!this.dragging) return; +    this.stopScrolling(); +    this.finishDrag(event, true); +    Event.stop(event); +  }, +   +  draw: function(point) { +    var pos = Position.cumulativeOffset(this.element); +    var d = this.currentDelta(); +    pos[0] -= d[0]; pos[1] -= d[1]; +     +    if(this.options.scroll && (this.options.scroll != window)) { +      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; +      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; +    } +     +    var p = [0,1].map(function(i){  +      return (point[i]-pos[i]-this.offset[i])  +    }.bind(this)); +     +    if(this.options.snap) { +      if(typeof this.options.snap == 'function') { +        p = this.options.snap(p[0],p[1],this); +      } else { +      if(this.options.snap instanceof Array) { +        p = p.map( function(v, i) { +          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) +      } else { +        p = p.map( function(v) { +          return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) +      } +    }} +     +    var style = this.element.style; +    if((!this.options.constraint) || (this.options.constraint=='horizontal')) +      style.left = p[0] + "px"; +    if((!this.options.constraint) || (this.options.constraint=='vertical')) +      style.top  = p[1] + "px"; +    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering +  }, +   +  stopScrolling: function() { +    if(this.scrollInterval) { +      clearInterval(this.scrollInterval); +      this.scrollInterval = null; +      Draggables._lastScrollPointer = null; +    } +  }, +   +  startScrolling: function(speed) { +    if(!(speed[0] || speed[1])) return; +    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; +    this.lastScrolled = new Date(); +    this.scrollInterval = setInterval(this.scroll.bind(this), 10); +  }, +   +  scroll: function() { +    var current = new Date(); +    var delta = current - this.lastScrolled; +    this.lastScrolled = current; +    if(this.options.scroll == window) { +      with (this._getWindowScroll(this.options.scroll)) { +        if (this.scrollSpeed[0] || this.scrollSpeed[1]) { +          var d = delta / 1000; +          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); +        } +      } +    } else { +      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; +      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000; +    } +     +    Position.prepare(); +    Droppables.show(Draggables._lastPointer, this.element); +    Draggables.notify('onDrag', this); +    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); +    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; +    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; +    if (Draggables._lastScrollPointer[0] < 0) +      Draggables._lastScrollPointer[0] = 0; +    if (Draggables._lastScrollPointer[1] < 0) +      Draggables._lastScrollPointer[1] = 0; +    this.draw(Draggables._lastScrollPointer); +     +    if(this.options.change) this.options.change(this); +  }, +   +  _getWindowScroll: function(w) { +    var T, L, W, H; +    with (w.document) { +      if (w.document.documentElement && documentElement.scrollTop) { +        T = documentElement.scrollTop; +        L = documentElement.scrollLeft; +      } else if (w.document.body) { +        T = body.scrollTop; +        L = body.scrollLeft; +      } +      if (w.innerWidth) { +        W = w.innerWidth; +        H = w.innerHeight; +      } else if (w.document.documentElement && documentElement.clientWidth) { +        W = documentElement.clientWidth; +        H = documentElement.clientHeight; +      } else { +        W = body.offsetWidth; +        H = body.offsetHeight +      } +    } +    return { top: T, left: L, width: W, height: H }; +  } +} + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create(); +SortableObserver.prototype = { +  initialize: function(element, observer) { +    this.element   = $(element); +    this.observer  = observer; +    this.lastValue = Sortable.serialize(this.element); +  }, +   +  onStart: function() { +    this.lastValue = Sortable.serialize(this.element); +  }, +   +  onEnd: function() { +    Sortable.unmark(); +    if(this.lastValue != Sortable.serialize(this.element)) +      this.observer(this.element) +  } +} + +var Sortable = { +  sortables: {}, +   +  _findRootElement: function(element) { +    while (element.tagName != "BODY") {   +      if(element.id && Sortable.sortables[element.id]) return element; +      element = element.parentNode; +    } +  }, + +  options: function(element) { +    element = Sortable._findRootElement($(element)); +    if(!element) return; +    return Sortable.sortables[element.id]; +  }, +   +  destroy: function(element){ +    var s = Sortable.options(element); +     +    if(s) { +      Draggables.removeObserver(s.element); +      s.droppables.each(function(d){ Droppables.remove(d) }); +      s.draggables.invoke('destroy'); +       +      delete Sortable.sortables[s.element.id]; +    } +  }, + +  create: function(element) { +    element = $(element); +    var options = Object.extend({  +      element:     element, +      tag:         'li',       // assumes li children, override with tag: 'tagname' +      dropOnEmpty: false, +      tree:        false, +      treeTag:     'ul', +      overlap:     'vertical', // one of 'vertical', 'horizontal' +      constraint:  'vertical', // one of 'vertical', 'horizontal', false +      containment: element,    // also takes array of elements (or id's); or false +      handle:      false,      // or a CSS class +      only:        false, +      hoverclass:  null, +      ghosting:    false, +      scroll:      false, +      scrollSensitivity: 20, +      scrollSpeed: 15, +      format:      /^[^_]*_(.*)$/, +      onChange:    Prototype.emptyFunction, +      onUpdate:    Prototype.emptyFunction +    }, arguments[1] || {}); + +    // clear any old sortable with same element +    this.destroy(element); + +    // build options for the draggables +    var options_for_draggable = { +      revert:      true, +      scroll:      options.scroll, +      scrollSpeed: options.scrollSpeed, +      scrollSensitivity: options.scrollSensitivity, +      ghosting:    options.ghosting, +      constraint:  options.constraint, +      handle:      options.handle }; + +    if(options.starteffect) +      options_for_draggable.starteffect = options.starteffect; + +    if(options.reverteffect) +      options_for_draggable.reverteffect = options.reverteffect; +    else +      if(options.ghosting) options_for_draggable.reverteffect = function(element) { +        element.style.top  = 0; +        element.style.left = 0; +      }; + +    if(options.endeffect) +      options_for_draggable.endeffect = options.endeffect; + +    if(options.zindex) +      options_for_draggable.zindex = options.zindex; + +    // build options for the droppables   +    var options_for_droppable = { +      overlap:     options.overlap, +      containment: options.containment, +      tree:        options.tree, +      hoverclass:  options.hoverclass, +      onHover:     Sortable.onHover +      //greedy:      !options.dropOnEmpty +    } +     +    var options_for_tree = { +      onHover:      Sortable.onEmptyHover, +      overlap:      options.overlap, +      containment:  options.containment, +      hoverclass:   options.hoverclass +    } + +    // fix for gecko engine +    Element.cleanWhitespace(element);  + +    options.draggables = []; +    options.droppables = []; + +    // drop on empty handling +    if(options.dropOnEmpty || options.tree) { +      Droppables.add(element, options_for_tree); +      options.droppables.push(element); +    } + +    (this.findElements(element, options) || []).each( function(e) { +      // handles are per-draggable +      var handle = options.handle ?  +        Element.childrenWithClassName(e, options.handle)[0] : e;     +      options.draggables.push( +        new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); +      Droppables.add(e, options_for_droppable); +      if(options.tree) e.treeNode = element; +      options.droppables.push(e);       +    }); +     +    if(options.tree) { +      (Sortable.findTreeElements(element, options) || []).each( function(e) { +        Droppables.add(e, options_for_tree); +        e.treeNode = element; +        options.droppables.push(e); +      }); +    } + +    // keep reference +    this.sortables[element.id] = options; + +    // for onupdate +    Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + +  }, + +  // return all suitable-for-sortable elements in a guaranteed order +  findElements: function(element, options) { +    return Element.findChildren( +      element, options.only, options.tree ? true : false, options.tag); +  }, +   +  findTreeElements: function(element, options) { +    return Element.findChildren( +      element, options.only, options.tree ? true : false, options.treeTag); +  }, + +  onHover: function(element, dropon, overlap) { +    if(Element.isParent(dropon, element)) return; + +    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { +      return; +    } else if(overlap>0.5) { +      Sortable.mark(dropon, 'before'); +      if(dropon.previousSibling != element) { +        var oldParentNode = element.parentNode; +        element.style.visibility = "hidden"; // fix gecko rendering +        dropon.parentNode.insertBefore(element, dropon); +        if(dropon.parentNode!=oldParentNode)  +          Sortable.options(oldParentNode).onChange(element); +        Sortable.options(dropon.parentNode).onChange(element); +      } +    } else { +      Sortable.mark(dropon, 'after'); +      var nextElement = dropon.nextSibling || null; +      if(nextElement != element) { +        var oldParentNode = element.parentNode; +        element.style.visibility = "hidden"; // fix gecko rendering +        dropon.parentNode.insertBefore(element, nextElement); +        if(dropon.parentNode!=oldParentNode)  +          Sortable.options(oldParentNode).onChange(element); +        Sortable.options(dropon.parentNode).onChange(element); +      } +    } +  }, +   +  onEmptyHover: function(element, dropon, overlap) { +    var oldParentNode = element.parentNode; +    var droponOptions = Sortable.options(dropon); +         +    if(!Element.isParent(dropon, element)) { +      var index; +       +      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); +      var child = null; +             +      if(children) { +        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); +         +        for (index = 0; index < children.length; index += 1) { +          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { +            offset -= Element.offsetSize (children[index], droponOptions.overlap); +          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { +            child = index + 1 < children.length ? children[index + 1] : null; +            break; +          } else { +            child = children[index]; +            break; +          } +        } +      } +       +      dropon.insertBefore(element, child); +       +      Sortable.options(oldParentNode).onChange(element); +      droponOptions.onChange(element); +    } +  }, + +  unmark: function() { +    if(Sortable._marker) Element.hide(Sortable._marker); +  }, + +  mark: function(dropon, position) { +    // mark on ghosting only +    var sortable = Sortable.options(dropon.parentNode); +    if(sortable && !sortable.ghosting) return;  + +    if(!Sortable._marker) { +      Sortable._marker = $('dropmarker') || document.createElement('DIV'); +      Element.hide(Sortable._marker); +      Element.addClassName(Sortable._marker, 'dropmarker'); +      Sortable._marker.style.position = 'absolute'; +      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); +    }     +    var offsets = Position.cumulativeOffset(dropon); +    Sortable._marker.style.left = offsets[0] + 'px'; +    Sortable._marker.style.top = offsets[1] + 'px'; +     +    if(position=='after') +      if(sortable.overlap == 'horizontal')  +        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px'; +      else +        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px'; +     +    Element.show(Sortable._marker); +  }, +   +  _tree: function(element, options, parent) { +    var children = Sortable.findElements(element, options) || []; +   +    for (var i = 0; i < children.length; ++i) { +      var match = children[i].id.match(options.format); + +      if (!match) continue; +       +      var child = { +        id: encodeURIComponent(match ? match[1] : null), +        element: element, +        parent: parent, +        children: new Array, +        position: parent.children.length, +        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase()) +      } +       +      /* Get the element containing the children and recurse over it */ +      if (child.container) +        this._tree(child.container, options, child) +       +      parent.children.push (child); +    } + +    return parent;  +  }, + +  /* Finds the first element of the given tag type within a parent element. +    Used for finding the first LI[ST] within a L[IST]I[TEM].*/ +  _findChildrenElement: function (element, containerTag) { +    if (element && element.hasChildNodes) +      for (var i = 0; i < element.childNodes.length; ++i) +        if (element.childNodes[i].tagName == containerTag) +          return element.childNodes[i]; +   +    return null; +  }, + +  tree: function(element) { +    element = $(element); +    var sortableOptions = this.options(element); +    var options = Object.extend({ +      tag: sortableOptions.tag, +      treeTag: sortableOptions.treeTag, +      only: sortableOptions.only, +      name: element.id, +      format: sortableOptions.format +    }, arguments[1] || {}); +     +    var root = { +      id: null, +      parent: null, +      children: new Array, +      container: element, +      position: 0 +    } +     +    return Sortable._tree (element, options, root); +  }, + +  /* Construct a [i] index for a particular node */ +  _constructIndex: function(node) { +    var index = ''; +    do { +      if (node.id) index = '[' + node.position + ']' + index; +    } while ((node = node.parent) != null); +    return index; +  }, + +  sequence: function(element) { +    element = $(element); +    var options = Object.extend(this.options(element), arguments[1] || {}); +     +    return $(this.findElements(element, options) || []).map( function(item) { +      return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; +    }); +  }, + +  setSequence: function(element, new_sequence) { +    element = $(element); +    var options = Object.extend(this.options(element), arguments[2] || {}); +     +    var nodeMap = {}; +    this.findElements(element, options).each( function(n) { +        if (n.id.match(options.format)) +            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; +        n.parentNode.removeChild(n); +    }); +    +    new_sequence.each(function(ident) { +      var n = nodeMap[ident]; +      if (n) { +        n[1].appendChild(n[0]); +        delete nodeMap[ident]; +      } +    }); +  }, +   +  serialize: function(element) { +    element = $(element); +    var options = Object.extend(Sortable.options(element), arguments[1] || {}); +    var name = encodeURIComponent( +      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); +     +    if (options.tree) { +      return Sortable.tree(element, arguments[1]).children.map( function (item) { +        return [name + Sortable._constructIndex(item) + "[id]=" +  +                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); +      }).flatten().join('&'); +    } else { +      return Sortable.sequence(element, arguments[1]).map( function(item) { +        return name + "[]=" + encodeURIComponent(item); +      }).join('&'); +    } +  } +} + +/* Returns true if child is contained within element */ +Element.isParent = function(child, element) { +  if (!child.parentNode || child == element) return false; + +  if (child.parentNode == element) return true; + +  return Element.isParent(child.parentNode, element); +} + +Element.findChildren = function(element, only, recursive, tagName) {     +  if(!element.hasChildNodes()) return null; +  tagName = tagName.toUpperCase(); +  if(only) only = [only].flatten(); +  var elements = []; +  $A(element.childNodes).each( function(e) { +    if(e.tagName && e.tagName.toUpperCase()==tagName && +      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) +        elements.push(e); +    if(recursive) { +      var grandchildren = Element.findChildren(e, only, recursive, tagName); +      if(grandchildren) elements.push(grandchildren); +    } +  }); + +  return (elements.length>0 ? elements.flatten() : []); +} + +Element.offsetSize = function (element, type) { +  if (type == 'vertical' || type == 'height') +    return element.offsetHeight; +  else +    return element.offsetWidth; +} + +// Copyright (c) 2005 Marty Haught, Thomas Fuchs  +// +// See http://script.aculo.us for more info +//  +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +//  +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if(!Control) var Control = {}; +Control.Slider = Class.create(); + +// options: +//  axis: 'vertical', or 'horizontal' (default) +// +// callbacks: +//  onChange(value) +//  onSlide(value) +Control.Slider.prototype = { +  initialize: function(handle, track, options) { +    var slider = this; +     +    if(handle instanceof Array) { +      this.handles = handle.collect( function(e) { return $(e) }); +    } else { +      this.handles = [$(handle)]; +    } +     +    this.track   = $(track); +    this.options = options || {}; + +    this.axis      = this.options.axis || 'horizontal'; +    this.increment = this.options.increment || 1; +    this.step      = parseInt(this.options.step || '1'); +    this.range     = this.options.range || $R(0,1); +     +    this.value     = 0; // assure backwards compat +    this.values    = this.handles.map( function() { return 0 }); +    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; +    this.options.startSpan = $(this.options.startSpan || null); +    this.options.endSpan   = $(this.options.endSpan || null); + +    this.restricted = this.options.restricted || false; + +    this.maximum   = this.options.maximum || this.range.end; +    this.minimum   = this.options.minimum || this.range.start; + +    // Will be used to align the handle onto the track, if necessary +    this.alignX = parseInt(this.options.alignX || '0'); +    this.alignY = parseInt(this.options.alignY || '0'); +     +    this.trackLength = this.maximumOffset() - this.minimumOffset(); + +    this.handleLength = this.isVertical() ?  +      (this.handles[0].offsetHeight != 0 ?  +        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :  +      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :  +        this.handles[0].style.width.replace(/px$/,"")); + +    this.active   = false; +    this.dragging = false; +    this.disabled = false; + +    if(this.options.disabled) this.setDisabled(); + +    // Allowed values array +    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; +    if(this.allowedValues) { +      this.minimum = this.allowedValues.min(); +      this.maximum = this.allowedValues.max(); +    } + +    this.eventMouseDown = this.startDrag.bindAsEventListener(this); +    this.eventMouseUp   = this.endDrag.bindAsEventListener(this); +    this.eventMouseMove = this.update.bindAsEventListener(this); + +    // Initialize handles in reverse (make sure first handle is active) +    this.handles.each( function(h,i) { +      i = slider.handles.length-1-i; +      slider.setValue(parseFloat( +        (slider.options.sliderValue instanceof Array ?  +          slider.options.sliderValue[i] : slider.options.sliderValue) ||  +         slider.range.start), i); +      Element.makePositioned(h); // fix IE +      Event.observe(h, "mousedown", slider.eventMouseDown); +    }); +     +    Event.observe(this.track, "mousedown", this.eventMouseDown); +    Event.observe(document, "mouseup", this.eventMouseUp); +    Event.observe(document, "mousemove", this.eventMouseMove); +     +    this.initialized = true; +  }, +  dispose: function() { +    var slider = this;     +    Event.stopObserving(this.track, "mousedown", this.eventMouseDown); +    Event.stopObserving(document, "mouseup", this.eventMouseUp); +    Event.stopObserving(document, "mousemove", this.eventMouseMove); +    this.handles.each( function(h) { +      Event.stopObserving(h, "mousedown", slider.eventMouseDown); +    }); +  }, +  setDisabled: function(){ +    this.disabled = true; +  }, +  setEnabled: function(){ +    this.disabled = false; +  },   +  getNearestValue: function(value){ +    if(this.allowedValues){ +      if(value >= this.allowedValues.max()) return(this.allowedValues.max()); +      if(value <= this.allowedValues.min()) return(this.allowedValues.min()); +       +      var offset = Math.abs(this.allowedValues[0] - value); +      var newValue = this.allowedValues[0]; +      this.allowedValues.each( function(v) { +        var currentOffset = Math.abs(v - value); +        if(currentOffset <= offset){ +          newValue = v; +          offset = currentOffset; +        }  +      }); +      return newValue; +    } +    if(value > this.range.end) return this.range.end; +    if(value < this.range.start) return this.range.start; +    return value; +  }, +  setValue: function(sliderValue, handleIdx){ +    if(!this.active) { +      this.activeHandleIdx = handleIdx || 0; +      this.activeHandle    = this.handles[this.activeHandleIdx]; +      this.updateStyles(); +    } +    handleIdx = handleIdx || this.activeHandleIdx || 0; +    if(this.initialized && this.restricted) { +      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1])) +        sliderValue = this.values[handleIdx-1]; +      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1])) +        sliderValue = this.values[handleIdx+1]; +    } +    sliderValue = this.getNearestValue(sliderValue); +    this.values[handleIdx] = sliderValue; +    this.value = this.values[0]; // assure backwards compat +     +    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =  +      this.translateToPx(sliderValue); +     +    this.drawSpans(); +    if(!this.dragging || !this.event) this.updateFinished(); +  }, +  setValueBy: function(delta, handleIdx) { +    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,  +      handleIdx || this.activeHandleIdx || 0); +  }, +  translateToPx: function(value) { +    return Math.round( +      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *  +      (value - this.range.start)) + "px"; +  }, +  translateToValue: function(offset) { +    return ((offset/(this.trackLength-this.handleLength) *  +      (this.range.end-this.range.start)) + this.range.start); +  }, +  getRange: function(range) { +    var v = this.values.sortBy(Prototype.K);  +    range = range || 0; +    return $R(v[range],v[range+1]); +  }, +  minimumOffset: function(){ +    return(this.isVertical() ? this.alignY : this.alignX); +  }, +  maximumOffset: function(){ +    return(this.isVertical() ?  +      (this.track.offsetHeight != 0 ? this.track.offsetHeight : +        this.track.style.height.replace(/px$/,"")) - this.alignY :  +      (this.track.offsetWidth != 0 ? this.track.offsetWidth :  +        this.track.style.width.replace(/px$/,"")) - this.alignY); +  },   +  isVertical:  function(){ +    return (this.axis == 'vertical'); +  }, +  drawSpans: function() { +    var slider = this; +    if(this.spans) +      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); +    if(this.options.startSpan) +      this.setSpan(this.options.startSpan, +        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); +    if(this.options.endSpan) +      this.setSpan(this.options.endSpan,  +        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); +  }, +  setSpan: function(span, range) { +    if(this.isVertical()) { +      span.style.top = this.translateToPx(range.start); +      span.style.height = this.translateToPx(range.end - range.start + this.range.start); +    } else { +      span.style.left = this.translateToPx(range.start); +      span.style.width = this.translateToPx(range.end - range.start + this.range.start); +    } +  }, +  updateStyles: function() { +    this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); +    Element.addClassName(this.activeHandle, 'selected'); +  }, +  startDrag: function(event) { +    if(Event.isLeftClick(event)) { +      if(!this.disabled){ +        this.active = true; +         +        var handle = Event.element(event); +        var pointer  = [Event.pointerX(event), Event.pointerY(event)]; +        var track = handle; +        if(track==this.track) { +          var offsets  = Position.cumulativeOffset(this.track);  +          this.event = event; +          this.setValue(this.translateToValue(  +           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) +          )); +          var offsets  = Position.cumulativeOffset(this.activeHandle); +          this.offsetX = (pointer[0] - offsets[0]); +          this.offsetY = (pointer[1] - offsets[1]); +        } else { +          // find the handle (prevents issues with Safari) +          while((this.handles.indexOf(handle) == -1) && handle.parentNode)  +            handle = handle.parentNode; +         +          this.activeHandle    = handle; +          this.activeHandleIdx = this.handles.indexOf(this.activeHandle); +          this.updateStyles(); +         +          var offsets  = Position.cumulativeOffset(this.activeHandle); +          this.offsetX = (pointer[0] - offsets[0]); +          this.offsetY = (pointer[1] - offsets[1]); +        } +      } +      Event.stop(event); +    } +  }, +  update: function(event) { +   if(this.active) { +      if(!this.dragging) this.dragging = true; +      this.draw(event); +      // fix AppleWebKit rendering +      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +      Event.stop(event); +   } +  }, +  draw: function(event) { +    var pointer = [Event.pointerX(event), Event.pointerY(event)]; +    var offsets = Position.cumulativeOffset(this.track); +    pointer[0] -= this.offsetX + offsets[0]; +    pointer[1] -= this.offsetY + offsets[1]; +    this.event = event; +    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); +    if(this.initialized && this.options.onSlide) +      this.options.onSlide(this.values.length>1 ? this.values : this.value, this); +  }, +  endDrag: function(event) { +    if(this.active && this.dragging) { +      this.finishDrag(event, true); +      Event.stop(event); +    } +    this.active = false; +    this.dragging = false; +  },   +  finishDrag: function(event, success) { +    this.active = false; +    this.dragging = false; +    this.updateFinished(); +  }, +  updateFinished: function() { +    if(this.initialized && this.options.onChange)  +      this.options.onChange(this.values.length>1 ? this.values : this.value, this); +    this.event = null; +  } +} + +/**
 + * Auto complete textbox via AJAX.
 + */
 +Prado.AutoCompleter = Class.create();
 +
 +
 +/**
 + * Overrides parent implementation of updateElement by trimming the value.
 + */
 +Prado.AutoCompleter.Base = function(){};
 +Prado.AutoCompleter.Base.prototype = Object.extend(Autocompleter.Base.prototype,
 +{
 +  updateElement: function(selectedElement) 
 +  {
 +    if (this.options.updateElement) {
 +      this.options.updateElement(selectedElement);
 +      return;
 +    }
 +
 +    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
 +    var lastTokenPos = this.findLastToken();
 +    if (lastTokenPos != -1) {
 +      var newValue = this.element.value.substr(0, lastTokenPos + 1);
 +      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
 +      if (whitespace)
 +        newValue += whitespace[0];
 +      this.element.value = (newValue + value).trim();
 +    } else {
 +      this.element.value = value.trim();
 +    }
 +    this.element.focus();
 +    
 +    if (this.options.afterUpdateElement)
 +      this.options.afterUpdateElement(this.element, selectedElement);
 +  }
 +});
 +
 +/**
 + * Based on the Prototype Autocompleter class.
 + * This client-side component should be instantiated from a Prado component.
 + * Usage: <t>new Prado.AutoCompleter('textboxID', 'updateDivID', {callbackID : '...'});
 + */
 +Prado.AutoCompleter.prototype = Object.extend(new Autocompleter.Base(),
 +{
 +	/**
 +	 * This component is initialized by
 +	 * <code>new Prado.AutoCompleter(...)</code>
 +	 * @param string the ID of the textbox element to observe
 +	 * @param string the ID of the div to display the auto-complete options
 +	 * @param array a hash of options, e.g. auto-completion token separator.
 +	 */
 +	initialize : function(element, update, options)
 +	{
 +		this.baseInitialize(element, update, options);
 +	},
 +
 +	/**
 +	 * The callback function, i.e., function called on successful AJAX return.
 +	 * Calls update choices in the Autocompleter.
 +	 * @param string new auto-complete options for display
 +	 */
 +	onUpdateReturn : function(result)
 +	{
 +		if(isString(result) && result.length > 0)
 +			this.updateChoices(result);
 +	},
 +
 +	/**
 +	 * Requesting new choices using Prado's client-side callback scheme.
 +	 */
 +	getUpdatedChoices : function()
 +	{
 +		Prado.Callback(this.element.id, this.getToken(), this.onUpdateReturn.bind(this));
 +	}
 +});
 +
 +/**
 + * Prado TActivePanel client javascript. Usage
 + * <code>
 + * Prado.ActivePanel.register("id", options);
 + * Prado.ActivePanel.update("id", "hello");
 + * </code>
 + */
 +Prado.ActivePanel =
 +{
 +	callbacks : {},
 +
 +	register : function(id, options)
 +	{
 +		Prado.ActivePanel.callbacks[id] = options;
 +	},
 +
 +	update : function(id, param)
 +	{
 +		var request = new Prado.ActivePanel.Request(id,
 +						Prado.ActivePanel.callbacks[id]);
 +		request.callback(param);
 +	}
 +}
 +
 +/**
 + * Client-script for TActivePanel. Uses Callback to notify the server
 + * for updates, if update option is set, the innerHTML of the update ID
 + * is set to the returned output.
 + */
 +Prado.ActivePanel.Request = Class.create();
 +Prado.ActivePanel.Request.prototype =
 +{
 +	initialize : function(element, options)
 +	{
 +		this.element = element;
 +		this.setOptions(options);
 +	},
 +
 +	/**
 +	 * Set some options.
 +	 */
 +	setOptions : function(options)
 +	{
 +		this.options =
 +		{
 +			onSuccess : this.onSuccess.bind(this)
 +		}
 +		Object.extend(this.options, options || {});
 +	},
 +
 +	/**
 +	 * Make the callback request
 +	 */
 +	callback : function(param)
 +	{
 +		this.options.params = [param];
 +		new Prado.AJAX.Callback(this.element, this.options);
 +	},
 +
 +	/**
 +	 * Callback onSuccess handler, update the element innerHTML if necessary
 +	 */
 +	onSuccess : function(result, output)
 +	{
 +		if(this.options.update)
 +		{
 +			if (!this.options.evalScripts)
 +				output = output.stripScripts();
 +			Element.update(this.options.update, output);
 +		}
 +	}
 +}
 +
 +/**
 + * Drop container to accept draggable component drops.
 + */
 +Prado.DropContainer = Class.create();
 +Prado.DropContainer.prototype = Object.extend(new Prado.ActivePanel.Request(),
 +{
 +	initialize : function(element, options)
 +	{
 +		this.element = element;
 +		this.setOptions(options);
 +		Object.extend(this.options,
 +		{
 +			onDrop : this.onDrop.bind(this),
 +			evalScripts : true,
 +			onSuccess : options.onSuccess || this.onSuccess.bind(this)
 +		});
 +		Droppables.add(element, this.options);
 +	},
 +
 +	onDrop : function(draggable, droppable)
 +	{
 +		this.callback(draggable.id)
 +	}
 +});
 +
 +Prado.ActiveImageButton = Class.create();
 +Prado.ActiveImageButton.prototype = 
 +{
 +	initialize : function(element, options)
 +	{
 +		this.element = $(element);
 +		this.options = options;
 +		Event.observe(this.element, "click", this.click.bind(this));
 +	},
 +
 +	click : function(e)
 +	{
 +		var el = $('{$this->ClientID}');
 +		var imagePos = Position.cumulativeOffset(this.element);
 +		var clickedPos = [e.clientX, e.clientY];
 +		var param = (clickedPos[0]-imagePos[0]+1)+","+(clickedPos[1]-imagePos[1]+1);
 +		Prado.Callback(this.element, param, null, this.options);
 +		Event.stop(e);
 +	}
 +}
 +
 +
 + + diff --git a/framework/Web/Javascripts/js/debug/clientscripts.php b/framework/Web/Javascripts/js/debug/clientscripts.php new file mode 100644 index 00000000..3ac3b062 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/clientscripts.php @@ -0,0 +1,61 @@ +<?php
 +/**
 + * This file compresses the javascript files using GZip
 + *
 + * Todo:
 + *  - Add local file cache for the GZip:ed version.
 + */
 +
 +$debugMode=(isset($_GET['mode']) && $_GET['mode']==='debug');
 +
 +// if debug mode, js is not cached; otherwise cached for 10 days.
 +$expiresOffset = $debugMode ? -10000 : 3600 * 24 * 10; //no cache
 +
 +//allowed libraries
 +$library = array('prado', 'effects', 'ajax', 'validator', 'logger', 'datepicker', 'rico', 'colorpicker');
 +
 +$param = isset($_GET['js']) ? $_GET['js'] : '';
 +
 +//check for proper matching parameters, otherwise exit;
 +if(preg_match('/(\w)+(,\w+)*/', $param)) $js = explode(',', $param); else exit();
 +foreach($js as $lib) if(!in_array($lib, $library)) exit();
 +
 +// Only gzip the contents if clients and server support it
 +if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
 +	$encodings = explode(',', strtolower($_SERVER['HTTP_ACCEPT_ENCODING']));
 +else
 +	$encodings = array();
 +
 +// Check for gzip header or northon internet securities
 +if ((in_array('gzip', $encodings) || isset($_SERVER['---------------']))
 +		&& function_exists('ob_gzhandler') && !ini_get('zlib.output_compression')
 +		&& ini_get('output_handler') != 'ob_gzhandler')
 +	ob_start("ob_gzhandler");
 +
 +// Output rest of headers
 +header('Content-type: text/javascript; charset: UTF-8');
 +// header("Cache-Control: must-revalidate");
 +header('Vary: Accept-Encoding'); // Handle proxies
 +header('Expires: ' . @gmdate('D, d M Y H:i:s', @time() + $expiresOffset) . ' GMT');
 +
 +if ($debugMode)
 +{
 +	foreach($js as $lib)
 +	{
 +		$file = realpath($lib.'.js');
 +		if(is_file($file))
 +			echo file_get_contents($file);
 +		else //log missings files to console logger
 +		{
 +			echo 'setTimeout(function(){ if(Logger) Logger.error("Missing file", "'.$lib.'.js"); }, 1000);';
 +			error_log("Unable to find asset file {$lib}.js");
 +		}
 +	}
 +}
 +else
 +{
 +	foreach($js as $lib)
 +		echo file_get_contents($lib.'.js');
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Web/Javascripts/js/debug/colorpicker.js b/framework/Web/Javascripts/js/debug/colorpicker.js new file mode 100644 index 00000000..b1b2aae4 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/colorpicker.js @@ -0,0 +1,778 @@ +//-------------------- ricoColor.js
 +if(typeof(Rico) == "undefined") Rico = {};
 +
 +Rico.Color = Class.create();
 +
 +Rico.Color.prototype = {
 +
 +   initialize: function(red, green, blue) {
 +      this.rgb = { r: red, g : green, b : blue };
 +   },
 +
 +   setRed: function(r) {
 +      this.rgb.r = r;
 +   },
 +
 +   setGreen: function(g) {
 +      this.rgb.g = g;
 +   },
 +
 +   setBlue: function(b) {
 +      this.rgb.b = b;
 +   },
 +
 +   setHue: function(h) {
 +
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.h = h;
 +
 +      // convert back to RGB...
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
 +   },
 +
 +   setSaturation: function(s) {
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.s = s;
 +
 +      // convert back to RGB and set values...
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
 +   },
 +
 +   setBrightness: function(b) {
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.b = b;
 +
 +      // convert back to RGB and set values...
 +      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
 +   },
 +
 +   darken: function(percent) {
 +      var hsb  = this.asHSB();
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
 +   },
 +
 +   brighten: function(percent) {
 +      var hsb  = this.asHSB();
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
 +   },
 +
 +   blend: function(other) {
 +      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
 +      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
 +      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
 +   },
 +
 +   isBright: function() {
 +      var hsb = this.asHSB();
 +      return this.asHSB().b > 0.5;
 +   },
 +
 +   isDark: function() {
 +      return ! this.isBright();
 +   },
 +
 +   asRGB: function() {
 +      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
 +   },
 +
 +   asHex: function() {
 +      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
 +   },
 +
 +   asHSB: function() {
 +      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
 +   },
 +
 +   toString: function() {
 +      return this.asHex();
 +   }
 +
 +};
 +
 +Rico.Color.createFromHex = function(hexCode) {
 +
 +   if ( hexCode.indexOf('#') == 0 )
 +      hexCode = hexCode.substring(1);
 +   
 +   var red = "ff", green = "ff", blue="ff";
 +   if(hexCode.length > 4)
 +	{
 +	   red   = hexCode.substring(0,2);
 +	   green = hexCode.substring(2,4);
 +	   blue  = hexCode.substring(4,6);
 +	}
 +	else if(hexCode.length > 0 & hexCode.length < 4)
 +	{
 +	  var r = hexCode.substring(0,1);
 +	  var g = hexCode.substring(1,2);
 +	  var b = hexCode.substring(2);
 +	  red = r+r;
 +	  green = g+g;
 +	  blue = b+b;
 +	}
 +   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
 +}
 +
 +/**
 + * Factory method for creating a color from the background of
 + * an HTML element.
 + */
 +Rico.Color.createColorFromBackground = function(elem) {
 +
 +   var actualColor = Element.getStyle($(elem), "background-color");
 +  if ( actualColor == "transparent" && elem.parent )
 +      return Rico.Color.createColorFromBackground(elem.parent);
 +
 +   if ( actualColor == null )
 +      return new Rico.Color(255,255,255);
 +
 +   if ( actualColor.indexOf("rgb(") == 0 ) {
 +      var colors = actualColor.substring(4, actualColor.length - 1 );
 +      var colorArray = colors.split(",");
 +      return new Rico.Color( parseInt( colorArray[0] ),
 +                            parseInt( colorArray[1] ),
 +                            parseInt( colorArray[2] )  );
 +
 +   }
 +   else if ( actualColor.indexOf("#") == 0 ) {
 +	  return Rico.Color.createFromHex(actualColor);
 +   }
 +   else
 +      return new Rico.Color(255,255,255);
 +}
 +
 +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
 +
 +   var red   = 0;
 +	var green = 0;
 +	var blue  = 0;
 +
 +   if (saturation == 0) {
 +      red = parseInt(brightness * 255.0 + 0.5);
 +	   green = red;
 +	   blue = red;
 +	}
 +	else {
 +      var h = (hue - Math.floor(hue)) * 6.0;
 +      var f = h - Math.floor(h);
 +      var p = brightness * (1.0 - saturation);
 +      var q = brightness * (1.0 - saturation * f);
 +      var t = brightness * (1.0 - (saturation * (1.0 - f)));
 +
 +      switch (parseInt(h)) {
 +         case 0:
 +            red   = (brightness * 255.0 + 0.5);
 +            green = (t * 255.0 + 0.5);
 +            blue  = (p * 255.0 + 0.5);
 +            break;
 +         case 1:
 +            red   = (q * 255.0 + 0.5);
 +            green = (brightness * 255.0 + 0.5);
 +            blue  = (p * 255.0 + 0.5);
 +            break;
 +         case 2:
 +            red   = (p * 255.0 + 0.5);
 +            green = (brightness * 255.0 + 0.5);
 +            blue  = (t * 255.0 + 0.5);
 +            break;
 +         case 3:
 +            red   = (p * 255.0 + 0.5);
 +            green = (q * 255.0 + 0.5);
 +            blue  = (brightness * 255.0 + 0.5);
 +            break;
 +         case 4:
 +            red   = (t * 255.0 + 0.5);
 +            green = (p * 255.0 + 0.5);
 +            blue  = (brightness * 255.0 + 0.5);
 +            break;
 +          case 5:
 +            red   = (brightness * 255.0 + 0.5);
 +            green = (p * 255.0 + 0.5);
 +            blue  = (q * 255.0 + 0.5);
 +            break;
 +	    }
 +	}
 +
 +   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
 +}
 +
 +Rico.Color.RGBtoHSB = function(r, g, b) {
 +
 +   var hue;
 +   var saturaton;
 +   var brightness;
 +
 +   var cmax = (r > g) ? r : g;
 +   if (b > cmax)
 +      cmax = b;
 +
 +   var cmin = (r < g) ? r : g;
 +   if (b < cmin)
 +      cmin = b;
 +
 +   brightness = cmax / 255.0;
 +   if (cmax != 0)
 +      saturation = (cmax - cmin)/cmax;
 +   else
 +      saturation = 0;
 +
 +   if (saturation == 0)
 +      hue = 0;
 +   else {
 +      var redc   = (cmax - r)/(cmax - cmin);
 +    	var greenc = (cmax - g)/(cmax - cmin);
 +    	var bluec  = (cmax - b)/(cmax - cmin);
 +
 +    	if (r == cmax)
 +    	   hue = bluec - greenc;
 +    	else if (g == cmax)
 +    	   hue = 2.0 + redc - bluec;
 +      else
 +    	   hue = 4.0 + greenc - redc;
 +
 +    	hue = hue / 6.0;
 +    	if (hue < 0)
 +    	   hue = hue + 1.0;
 +   }
 +
 +   return { h : hue, s : saturation, b : brightness };
 +}
 + + +Prado.WebUI.TColorPicker = Class.create();
 +
 +Object.extend(Prado.WebUI.TColorPicker,
 +{
 +	palettes: 
 +	{
 +		Small : [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"],
 +				["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"],
 +				["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"],
 +				["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"],
 +				["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"],
 +				["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"],
 +				["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]],
 +
 +		Tiny : [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/],
 +				["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/],
 +				["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]]
 +	},
 +
 +	UIImages : 
 +	{
 +		'button.gif' : 'button.gif',
 +//		'target_black.gif' : 'target_black.gif',
 +//		'target_white.gif' : 'target_white.gif',
 +		'background.png' : 'background.png'
 +//		'slider.gif' : 'slider.gif',
 +//		'hue.gif' : 'hue.gif'
 +	}
 +});
 +
 +Object.extend(Prado.WebUI.TColorPicker.prototype, 
 +{
 +	initialize : function(options)
 +	{
 +		var basics = 
 +		{
 +			Palette : 'Small',
 +			ClassName : 'TColorPicker',
 +			Mode : 'Basic',
 +			OKButtonText : 'OK',
 +			CancelButtonText : 'Cancel',
 +			ShowColorPicker : true
 +		}
 +		
 +		this.element = null;
 +		this.showing = false;
 +
 +		options = Object.extend(basics, options);
 +		this.options = options;
 +		this.input = $(options['ID']);
 +		this.button = $(options['ID']+'_button');
 +		this._buttonOnClick = this.buttonOnClick.bind(this);
 +		if(options['ShowColorPicker'])
 +			Event.observe(this.button, "click", this._buttonOnClick);
 +		Event.observe(this.input, "change", this.updatePicker.bind(this));
 +	},
 +
 +	updatePicker : function(e)
 +	{
 +		var color = Rico.Color.createFromHex(this.input.value);	
 +		this.button.style.backgroundColor = color.toString();
 +	},
 +	
 +	buttonOnClick : function(event)
 +	{
 +		var mode = this.options['Mode'];
 +		if(this.element == null)
 +		{
 +			var constructor = mode == "Basic" ? "getBasicPickerContainer": "getFullPickerContainer"
 +			this.element = this[constructor](this.options['ID'], this.options['Palette'])
 +			this.input.parentNode.appendChild(this.element);
 +			this.element.style.display = "none";
 +
 +			if(Prado.Browser().ie)
 +			{
 +				this.iePopUp = document.createElement('iframe');
 +				this.iePopUp.src = Prado.WebUI.TColorPicker.UIImages['button.gif'];
 +				this.iePopUp.style.position = "absolute"
 +				this.iePopUp.scrolling="no"
 +				this.iePopUp.frameBorder="0"
 +				this.input.parentNode.appendChild(this.iePopUp);
 +			}
 +			if(mode == "Full")
 +				this.initializeFullPicker();
 +		}
 +		this.show(mode);
 +	},		
 +
 +	show : function(type)
 +	{
 +		if(!this.showing)
 +		{
 +			var pos = Position.positionedOffset(this.input);
 +			pos[1] += this.input.offsetHeight;
 +
 +			this.element.style.top = (pos[1]-1) + "px";
 +			this.element.style.left = pos[0] + "px";
 +			this.element.style.display = "block";
 +			
 +			this.ieHack(type);
 +
 +			//observe for clicks on the document body
 +			this._documentClickEvent = this.hideOnClick.bindEvent(this, type);
 +			this._documentKeyDownEvent = this.keyPressed.bindEvent(this, type);
 +			Event.observe(document.body, "click", this._documentClickEvent);
 +			Event.observe(document,"keydown", this._documentKeyDownEvent); 
 +			this.showing = true;
 +
 +			if(type == "Full")
 +			{
 +				this.observeMouseMovement();
 +				var color = Rico.Color.createFromHex(this.input.value);
 +				this.inputs.oldColor.style.backgroundColor = color.asHex();
 +				this.setColor(color,true);
 +			}
 +		}
 +	},
 +	
 +	hide : function(event)
 +	{
 +		if(this.showing)
 +		{
 +			if(this.iePopUp)
 +				this.iePopUp.style.display = "none";
 +
 +			this.element.style.display = "none";
 +			this.showing = false;
 +			Event.stopObserving(document.body, "click", this._documentClickEvent);
 +			Event.stopObserving(document,"keydown", this._documentKeyDownEvent); 
 +			
 +			if(this._observingMouseMove)
 +			{			
 +				Event.stopObserving(document.body, "mousemove", this._onMouseMove);	
 +				this._observingMouseMove = false;
 +			}
 +		}
 +	},
 +
 +	keyPressed : function(event,type)
 +	{
 +		if(Event.keyCode(event) == Event.KEY_ESC)
 +			this.hide(event,type);
 +	},
 +
 +	hideOnClick : function(ev)
 +	{
 +		if(!this.showing) return;
 +		var el = Event.element(ev);
 +		var within = false;
 +		do
 +		{	within = within || String(el.className).indexOf('FullColorPicker') > -1
 +			within = within || el == this.button;
 +			within = within || el == this.input;
 +			if(within) break;
 +			el = el.parentNode;			
 +		}
 +		while(el);
 +		if(!within) this.hide(ev);
 +	},
 +
 +	ieHack : function() 
 +	{
 +		// IE hack
 +		if(this.iePopUp) 
 +		{
 +			this.iePopUp.style.display = "block";
 +			this.iePopUp.style.top = (this.element.offsetTop) + "px";
 +			this.iePopUp.style.left = (this.element.offsetLeft)+ "px";
 +			this.iePopUp.style.width = Math.abs(this.element.offsetWidth)+ "px";
 +			this.iePopUp.style.height = (this.element.offsetHeight + 1)+ "px";
 +		}
 +	},
 +
 +	getBasicPickerContainer : function(pickerID, palette)
 +	{
 +		var table = TABLE({className:'basic_colors palette_'+palette},TBODY());
 +		var colors = Prado.WebUI.TColorPicker.palettes[palette];
 +		var pickerOnClick = this.cellOnClick.bind(this);
 +		colors.each(function(color)
 +		{
 +			var row = document.createElement("tr");
 +			color.each(function(c)
 +			{
 +				var td = document.createElement("td");
 +				var img = IMG({src:Prado.WebUI.TColorPicker.UIImages['button.gif'],width:16,height:16});
 +				img.style.backgroundColor = "#"+c;
 +				Event.observe(img,"click", pickerOnClick);
 +				Event.observe(img,"mouseover", function(e)
 +				{
 +					Element.addClassName(Event.element(e), "pickerhover");
 +				});
 +				Event.observe(img,"mouseout", function(e)
 +				{
 +					Element.removeClassName(Event.element(e), "pickerhover");
 +				});
 +				td.appendChild(img);
 +				row.appendChild(td);
 +			});
 +			table.childNodes[0].appendChild(row);
 +		});
 +		return DIV({className:this.options['ClassName']+" BasicColorPicker",
 +					id:pickerID+"_picker"}, table);
 +	},
 +
 +	cellOnClick : function(e)
 +	{
 +		var el = Event.element(e); 
 +		if(el.tagName.toLowerCase() != "img")
 +			return;
 +		var color = Rico.Color.createColorFromBackground(el);
 +		this.updateColor(color);
 +	},
 +
 +	updateColor : function(color)
 +	{
 +		this.input.value = color.toString().toUpperCase();
 +		this.button.style.backgroundColor = color.toString();
 +		if(typeof(this.onChange) == "function")
 +			this.onChange(color);
 +	},
 +
 +	getFullPickerContainer : function(pickerID)
 +	{			
 +		//create the 3 buttons
 +		this.buttons = 
 +		{
 +			//Less   : INPUT({value:'Less Colors', className:'button', type:'button'}),
 +			OK	   : INPUT({value:this.options.OKButtonText, className:'button', type:'button'}),
 +			Cancel : INPUT({value:this.options.CancelButtonText, className:'button', type:'button'})
 +		};
 +		
 +		//create the 6 inputs
 +		var inputs = {};
 +		['H','S','V','R','G','B'].each(function(type)
 +		{
 +			inputs[type] = INPUT({type:'text',size:'3',maxlength:'3'});
 +		});
 +
 +		//create the HEX input
 +		inputs['HEX'] = INPUT({className:'hex',type:'text',size:'6',maxlength:'6'});
 +		this.inputs = inputs;
 +		
 +		var images = Prado.WebUI.TColorPicker.UIImages;
 +
 +		this.inputs['currentColor'] = SPAN({className:'currentColor'});
 +		this.inputs['oldColor'] = SPAN({className:'oldColor'});
 +
 +		var inputsTable = 
 +			TABLE({className:'inputs'}, TBODY(null,
 +				TR(null,
 +					TD({className:'currentcolor',colSpan:2},
 +						this.inputs['currentColor'], this.inputs['oldColor'])),
 +
 +				TR(null,
 +					TD(null,'H:'),
 +					TD(null,this.inputs['H'], '??')),
 +
 +				TR(null,
 +					TD(null,'S:'),
 +					TD(null,this.inputs['S'], '%')),
 +				
 +				TR(null, 
 +					TD(null,'V:'),
 +					TD(null,this.inputs['V'], '%')),
 +				
 +				TR(null, 
 +					TD({className:'gap'},'R:'),
 +					TD({className:'gap'},this.inputs['R'])),
 +				
 +				TR(null, 
 +					TD(null,'G:'),
 +					TD(null, this.inputs['G'])),
 +
 +				TR(null, 
 +					TD(null,'B:'),
 +					TD(null, this.inputs['B'])),
 +
 +				TR(null, 
 +					TD({className:'gap'},'#'),
 +					TD({className:'gap'},this.inputs['HEX']))
 +			));
 +
 +		var UIimages = 
 +		{		
 +			selector : SPAN({className:'selector'}),
 +			background : SPAN({className:'colorpanel'}),
 +			slider : SPAN({className:'slider'}),
 +			hue : SPAN({className:'strip'})
 +		}
 +
 +		//png alpha channels for IE
 +		if(Prado.Browser().ie)
 +		{
 +			var filter = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader";
 +			UIimages['background'] = SPAN({className:'colorpanel',style:filter+"(src='"+images['background.png']+"' sizingMethod=scale);"})
 +		}
 +		
 +		this.inputs = Object.extend(this.inputs, UIimages);
 +
 +		var pickerTable = 
 +			TABLE(null,TBODY(null,
 +				TR({className:'selection'},
 +					TD({className:'colors'},UIimages['selector'],UIimages['background']),
 +					TD({className:'hue'},UIimages['slider'],UIimages['hue']),
 +					TD({className:'inputs'}, inputsTable)
 +				),
 +				TR({className:'options'}, 
 +					TD({colSpan:3}, 
 +						this.buttons['OK'], 
 +						this.buttons['Cancel'])
 +				)
 +			));
 +		
 +		return DIV({className:this.options['ClassName']+" FullColorPicker",
 +						id:pickerID+"_picker"},pickerTable);
 +	},
 +
 +	initializeFullPicker : function()
 +	{
 +		var color = Rico.Color.createFromHex(this.input.value);
 +		this.inputs.oldColor.style.backgroundColor = color.asHex();
 +		this.setColor(color,true);
 +
 +		var i = 0; 
 +		for(var type in this.inputs)
 +		{
 +			Event.observe(this.inputs[type], "change", 
 +				this.onInputChanged.bindEvent(this,type));
 +			i++;
 +
 +			if(i > 6) break;
 +		}
 +	
 +		this.isMouseDownOnColor = false;
 +		this.isMouseDownOnHue = false;
 +
 +		this._onColorMouseDown = this.onColorMouseDown.bind(this);
 +		this._onHueMouseDown = this.onHueMouseDown.bind(this);
 +		this._onMouseUp = this.onMouseUp.bind(this);
 +		this._onMouseMove = this.onMouseMove.bind(this);
 +
 +		Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown);
 +		Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown);
 +		Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown);
 +		Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown);
 +		
 +		Event.observe(document.body, "mouseup", this._onMouseUp);
 +		
 +		this.observeMouseMovement();
 +
 +		Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode']));
 +		Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this));
 +	},
 +
 +	observeMouseMovement : function()
 +	{
 +		if(!this._observingMouseMove)
 +		{
 +			Event.observe(document.body, "mousemove", this._onMouseMove);
 +			this._observingMouseMove = true;
 +		}		
 +	},
 +
 +	onColorMouseDown : function(ev)
 +	{
 +		this.isMouseDownOnColor = true;
 +		this.onMouseMove(ev);
 +		Event.stop(ev);
 +	},
 +
 +	onHueMouseDown : function(ev)
 +	{
 +		this.isMouseDownOnHue = true;
 +		this.onMouseMove(ev);
 +		Event.stop(ev);
 +	},
 +
 +	onMouseUp : function(ev)
 +	{
 +		this.isMouseDownOnColor = false;
 +		this.isMouseDownOnHue = false;
 +		Event.stop(ev);
 +	},
 +
 +	onMouseMove : function(ev)
 +	{
 +		if(this.isMouseDownOnColor)
 +			this.changeSV(ev);
 +		if(this.isMouseDownOnHue)
 +			this.changeH(ev);
 +		Event.stop(ev);
 +	},	
 +
 +	changeSV : function(ev)
 +	{
 +		var px = Event.pointerX(ev);
 +		var py = Event.pointerY(ev);
 +		var pos = Position.cumulativeOffset(this.inputs.background);
 +		
 +		var x = this.truncate(px - pos[0],0,255); 
 +		var y = this.truncate(py - pos[1],0,255);
 +
 +
 +		var s = x/255;
 +		var b = (255-y)/255;
 +
 +		var current_s = parseInt(this.inputs.S.value);
 +		var current_b = parseInt(this.inputs.V.value);
 +		
 +		if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return;
 +
 +		var h = this.truncate(this.inputs.H.value,0,360)/360;
 +
 +		var color = new Rico.Color();
 +		color.rgb = Rico.Color.HSBtoRGB(h,s,b);
 +
 +
 +		this.inputs.selector.style.left = x+"px";
 +		this.inputs.selector.style.top = y+"px";
 +		
 +		this.inputs.currentColor.style.backgroundColor = color.asHex();
 +
 +		return this.setColor(color);
 +	},
 +
 +	changeH : function(ev)
 +	{
 +		var py = Event.pointerY(ev);
 +		var pos = Position.cumulativeOffset(this.inputs.background);
 +		var y = this.truncate(py - pos[1],0,255);
 +		
 +		var h = (255-y)/255;
 +		var current_h = this.truncate(this.inputs.H.value,0,360);
 +		current_h = current_h == 0 ? 360 : current_h;
 +		if(current_h == parseInt(h*360)) return;
 +
 +		var s = parseInt(this.inputs.S.value)/100;
 +		var b = parseInt(this.inputs.V.value)/100;
 +		var color = new Rico.Color();
 +		color.rgb = Rico.Color.HSBtoRGB(h,s,b);
 +
 +		var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b);
 +		hue.setSaturation(1); hue.setBrightness(1);
 +
 +		this.inputs.background.style.backgroundColor = hue.asHex();
 +		this.inputs.currentColor.style.backgroundColor = color.asHex();
 +
 +		this.inputs.slider.style.top = this.truncate(y,0,255)+"px";
 +		return this.setColor(color);
 +
 +	},
 +
 +	onOKClicked : function(ev)
 +	{
 +		var r = this.truncate(this.inputs.R.value,0,255);///255;
 +		var g = this.truncate(this.inputs.G.value,0,255);///255;
 +		var b = this.truncate(this.inputs.B.value,0,255);///255;
 +		var color = new Rico.Color(r,g,b);
 +		this.updateColor(color);
 +		this.inputs.oldColor.style.backgroundColor = color.asHex();
 +		this.hide(ev);
 +	},
 +
 +	onInputChanged : function(ev, type)
 +	{
 +		if(this.isMouseDownOnColor || isMouseDownOnHue)
 +			return;
 +
 +
 +		switch(type)
 +		{
 +			case "H": case "S": case "V":
 +				var h = this.truncate(this.inputs.H.value,0,360)/360;
 +				var s = this.truncate(this.inputs.S.value,0,100)/100;
 +				var b = this.truncate(this.inputs.V.value,0,100)/100;
 +				var color = new Rico.Color();
 +				color.rgb = Rico.Color.HSBtoRGB(h,s,b);
 +				return this.setColor(color,true);
 +			case "R": case "G": case "B":
 +				var r = this.truncate(this.inputs.R.value,0,255);///255;
 +				var g = this.truncate(this.inputs.G.value,0,255);///255;
 +				var b = this.truncate(this.inputs.B.value,0,255);///255;
 +				var color = new Rico.Color(r,g,b);
 +				return this.setColor(color,true);
 +			case "HEX":
 +				var color = Rico.Color.createFromHex(this.inputs.HEX.value);
 +				return this.setColor(color,true);
 +		}
 +	},
 +
 +	setColor : function(color, update)
 +	{
 +		var hsb = color.asHSB();
 +
 +		this.inputs.H.value = parseInt(hsb.h*360);
 +		this.inputs.S.value = parseInt(hsb.s*100);
 +		this.inputs.V.value = parseInt(hsb.b*100);
 +		this.inputs.R.value = color.rgb.r;
 +		this.inputs.G.value = color.rgb.g;
 +		this.inputs.B.value = color.rgb.b;
 +		this.inputs.HEX.value = color.asHex().substring(1).toUpperCase();
 +		
 +		var images = Prado.WebUI.TColorPicker.UIImages;
 +
 +		var changeCss = color.isBright() ? 'removeClassName' : 'addClassName';
 +		Element[changeCss](this.inputs.selector, 'target_white');
 +		
 +		if(update)
 +			this.updateSelectors(color);
 +	},
 +
 +	updateSelectors : function(color)
 +	{
 +		var hsb = color.asHSB();
 +		var pos = [hsb.s*255, hsb.b*255, hsb.h*255];
 +		
 +		this.inputs.selector.style.left = this.truncate(pos[0],0,255)+"px";
 +		this.inputs.selector.style.top = this.truncate(255-pos[1],0,255)+"px";
 +		this.inputs.slider.style.top = this.truncate(255-pos[2],0,255)+"px";
 +		
 +		var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b);
 +		hue.setSaturation(1); hue.setBrightness(1);
 +		this.inputs.background.style.backgroundColor = hue.asHex();
 +		this.inputs.currentColor.style.backgroundColor = color.asHex();
 +	},
 +
 +	truncate : function(value, min, max)
 +	{
 +		value = parseInt(value);
 +		return value < min ? min : value > max ? max : value;
 +	}
 +});
 + + diff --git a/framework/Web/Javascripts/js/debug/datepicker.js b/framework/Web/Javascripts/js/debug/datepicker.js new file mode 100644 index 00000000..0e21b2da --- /dev/null +++ b/framework/Web/Javascripts/js/debug/datepicker.js @@ -0,0 +1,695 @@ +Prado.WebUI.TDatePicker = Class.create();
 +Object.extend(Prado.WebUI.TDatePicker,
 +{
 +	/**
 +	 * @return Date the date from drop down list options.
 +	 */
 +	getDropDownDate : function(control)
 +	{
 +		var now=new Date();
 +		var year=now.getFullYear();
 +		var month=now.getMonth();
 +		var day=1;
 +
 +		var month_list = this.getMonthListControl(control);
 +	 	var day_list = this.getDayListControl(control);
 +	 	var year_list = this.getYearListControl(control);
 +
 +		var day = day_list ? $F(day_list) : 1;
 +		var month = month_list ? $F(month_list) : now.getMonth();
 +		var year = year_list ? $F(year_list) : now.getFullYear();
 +
 +		return new Date(year,month,day, 0, 0, 0);
 +	},
 +
 +	getYearListControl : function(control)
 +	{
 +		return $(control.id+"_year");
 +	},
 +
 +	getMonthListControl : function(control)
 +	{
 +		return $(control.id+"_month");
 +	},
 +
 +	getDayListControl : function(control)
 +	{
 +		return $(control.id+"_day");
 +	}
 +});
 +
 +Prado.WebUI.TDatePicker.prototype =
 +{
 +	MonthNames : [	"January",		"February",		"March",	"April",
 +		"May",			"June",			"July",		"August",
 +		"September",	"October",		"November",	"December"
 +	],
 +	AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May",
 +						"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
 +
 +	ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
 +
 +	Format : "yyyy-MM-dd",
 +
 +	FirstDayOfWeek : 1, // 0 for sunday
 +
 +	ClassName : "TDatePicker",
 +
 +	FromYear : 2000, UpToYear: 2015,
 +
 +	initialize : function(options)
 +	{
 +		this.options = options || [];
 +		this.control = $(options.ID);
 +		this.dateSlot = new Array(42);
 +		this.weekSlot = new Array(6);
 +		this.minimalDaysInFirstWeek	= 4;
 +		this.selectedDate = this.newDate();
 +
 +		//which element to trigger to show the calendar
 +		if(this.options.Trigger)
 +		{
 +			this.trigger = $(this.options.Trigger) ;
 +			var triggerEvent = this.options.TriggerEvent || "click";
 +		}
 +		else
 +		{
 +			this.trigger  = this.control;
 +			var triggerEvent = this.options.TriggerEvent || "focus";
 +		}
 +
 +		Object.extend(this,options);
 +
 +		Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this));
 +
 +	},
 +
 +	create : function()
 +	{
 +		if(typeof(this._calDiv) != "undefined")
 +			return;
 +
 +		var div;
 +		var table;
 +		var tbody;
 +		var tr;
 +		var td;
 +
 +		// Create the top-level div element
 +		this._calDiv = document.createElement("div");
 +		this._calDiv.className = this.ClassName;
 +		this._calDiv.style.display = "none";
 +		this._calDiv.style.position = "absolute"
 +
 +		// header div
 +		div = document.createElement("div");
 +		div.className = "calendarHeader";
 +		this._calDiv.appendChild(div);
 +
 +		table = document.createElement("table");
 +		table.style.cellSpacing = 0;
 +		div.appendChild(table);
 +
 +		tbody = document.createElement("tbody");
 +		table.appendChild(tbody);
 +
 +		tr = document.createElement("tr");
 +		tbody.appendChild(tr);
 +
 +		// Previous Month Button
 +		td = document.createElement("td");
 +		var previousMonth = document.createElement("input");
 +		previousMonth.className = "prevMonthButton button";
 +		previousMonth.type = "button"
 +		previousMonth.value = "<<";
 +		td.appendChild(previousMonth);
 +		tr.appendChild(td);
 +
 +
 +
 +		//
 +		// Create the month drop down
 +		//
 +		td = document.createElement("td");
 +		tr.appendChild(td);
 +		this._monthSelect = document.createElement("select");
 +		this._monthSelect.className = "months";
 +	    for (var i = 0 ; i < this.MonthNames.length ; i++) {
 +	        var opt = document.createElement("option");
 +	        opt.innerHTML = this.MonthNames[i];
 +	        opt.value = i;
 +	        if (i == this.selectedDate.getMonth()) {
 +	            opt.selected = true;
 +	        }
 +	        this._monthSelect.appendChild(opt);
 +	    }
 +		td.appendChild(this._monthSelect);
 +
 +
 +		//
 +		// Create the year drop down
 +		//
 +		td = document.createElement("td");
 +		td.className = "labelContainer";
 +		tr.appendChild(td);
 +		this._yearSelect = document.createElement("select");
 +		for(var i=this.FromYear; i <= this.UpToYear; ++i) {
 +			var opt = document.createElement("option");
 +			opt.innerHTML = i;
 +			opt.value = i;
 +			if (i == this.selectedDate.getFullYear()) {
 +				opt.selected = false;
 +			}
 +			this._yearSelect.appendChild(opt);
 +		}
 +		td.appendChild(this._yearSelect);
 +
 +
 +		td = document.createElement("td");
 +		var nextMonth = document.createElement("input");
 +		nextMonth.className = "nextMonthButton button";
 +		nextMonth.type = "button";
 +		nextMonth.value = ">>";
 +		td.appendChild(nextMonth);
 +		tr.appendChild(td);
 +
 +		// Calendar body
 +		div = document.createElement("div");
 +		div.className = "calendarBody";
 +		this._calDiv.appendChild(div);
 +		var calendarBody = div;
 +
 +		// Create the inside of calendar body
 +
 +		var text;
 +		table = document.createElement("table");
 +		table.align="center";
 +		table.className = "grid";
 +
 +	    div.appendChild(table);
 +		var thead = document.createElement("thead");
 +		table.appendChild(thead);
 +		tr = document.createElement("tr");
 +		thead.appendChild(tr);
 +
 +		for(i=0; i < 7; ++i) {
 +			td = document.createElement("th");
 +			text = document.createTextNode(this.ShortWeekDayNames[(i+this.FirstDayOfWeek)%7]);
 +			td.appendChild(text);
 +			td.className = "weekDayHead";
 +			tr.appendChild(td);
 +		}
 +
 +		// Date grid
 +		tbody = document.createElement("tbody");
 +		table.appendChild(tbody);
 +
 +		for(week=0; week<6; ++week) {
 +			tr = document.createElement("tr");
 +			tbody.appendChild(tr);
 +
 +		for(day=0; day<7; ++day) {
 +				td = document.createElement("td");
 +				td.className = "calendarDate";
 +				text = document.createTextNode(String.fromCharCode(160));
 +				td.appendChild(text);
 +
 +				tr.appendChild(td);
 +				var tmp = new Object();
 +				tmp.tag = "DATE";
 +				tmp.value = -1;
 +				tmp.data = text;
 +				this.dateSlot[(week*7)+day] = tmp;
 +
 +				Event.observe(td, "mouseover", this.hover.bindEvent(this));
 +				Event.observe(td, "mouseout", this.hover.bindEvent(this));
 +
 +			}
 +		}
 +
 +		// Calendar Footer
 +		div = document.createElement("div");
 +		div.className = "calendarFooter";
 +		this._calDiv.appendChild(div);
 +
 +		var todayButton = document.createElement("input");
 +		todayButton.type="button";
 +		todayButton.className = "todayButton";
 +		var today = this.newDate();
 +		var buttonText = today.SimpleFormat(this.Format,this);
 +		todayButton.value = buttonText;
 +		div.appendChild(todayButton);
 +
 +		if(Prado.Browser().ie)
 +		{
 +			this.iePopUp = document.createElement('iframe');
 +			this.iePopUp.src = Prado.WebUI.TDatePicker.spacer;
 +			this.iePopUp.style.position = "absolute"
 +			this.iePopUp.scrolling="no"
 +			this.iePopUp.frameBorder="0"
 +			this.control.parentNode.appendChild(this.iePopUp);
 +		}
 +
 +		this.control.parentNode.appendChild(this._calDiv);
 +
 +		this.update();
 +		this.updateHeader();
 +
 +		this.ieHack(true);
 +
 +		// IE55+ extension
 +		previousMonth.hideFocus = true;
 +		nextMonth.hideFocus = true;
 +		todayButton.hideFocus = true;
 +		// end IE55+ extension
 +
 +		// hook up events
 +		Event.observe(previousMonth, "click", this.prevMonth.bindEvent(this));
 +		Event.observe(nextMonth, "click", this.nextMonth.bindEvent(this));
 +		Event.observe(todayButton, "click", this.selectToday.bindEvent(this));
 +		//Event.observe(clearButton, "click", this.clearSelection.bindEvent(this));
 +		Event.observe(this._monthSelect, "change", this.monthSelect.bindEvent(this));
 +		Event.observe(this._yearSelect, "change", this.yearSelect.bindEvent(this));
 +
 +		// ie6 extension
 +		Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this));
 +
 +		Event.observe(calendarBody, "click", this.selectDate.bindEvent(this));
 +
 +		Prado.Element.focus(this.control);
 +
 +	},
 +
 +	ieHack : function(cleanup)
 +	{
 +		// IE hack
 +		if(this.iePopUp)
 +		{
 +			this.iePopUp.style.display = "block";
 +			this.iePopUp.style.top = (this._calDiv.offsetTop -1 ) + "px";
 +			this.iePopUp.style.left = (this._calDiv.offsetLeft -1)+ "px";
 +			this.iePopUp.style.width = Math.abs(this._calDiv.offsetWidth -2)+ "px";
 +			this.iePopUp.style.height = (this._calDiv.offsetHeight + 1)+ "px";
 +			if(cleanup) this.iePopUp.style.display = "none";
 +		}
 +	},
 +
 +	keyPressed : function(ev)
 +	{
 +		if(!this.showing) return;
 +		if (!ev) ev = document.parentWindow.event;
 +		var kc = ev.keyCode != null ? ev.keyCode : ev.charCode;
 +
 +		if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR || kc == Event.KEY_TAB)
 +		{
 +			this.setSelectedDate(this.selectedDate);
 +			Event.stop(ev);
 +			this.hide();
 +		}
 +		if(kc == Event.KEY_ESC)
 +		{
 +			Event.stop(ev); this.hide();
 +		}
 +
 +		var getDaysPerMonth = function (nMonth, nYear)
 +		{
 +			nMonth = (nMonth + 12) % 12;
 +	        var days= [31,28,31,30,31,30,31,31,30,31,30,31];
 +			var res = days[nMonth];
 +			if (nMonth == 1) //feburary, leap years has 29
 +                res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0;
 +	        return res;
 +		}
 +
 +		if(kc < 37 || kc > 40) return true;
 +
 +		var current = this.selectedDate;
 +		var d = current.valueOf();
 +		if(kc == Event.KEY_LEFT)
 +		{
 +			if(ev.ctrlKey || ev.shiftKey) // -1 month
 +			{
 +                current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() - 1,current.getFullYear())) ); // no need to catch dec -> jan for the year
 +                d = current.setMonth( current.getMonth() - 1 );
 +			}
 +			else
 +				d -= 86400000; //-1 day
 +		}
 +		else if (kc == Event.KEY_RIGHT)
 +		{
 +			if(ev.ctrlKey || ev.shiftKey) // +1 month
 +			{
 +				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() + 1,current.getFullYear())) ); // no need to catch dec -> jan for the year
 +				d = current.setMonth( current.getMonth() + 1 );
 +			}
 +			else
 +				d += 86400000; //+1 day
 +		}
 +		else if (kc == Event.KEY_UP)
 +		{
 +			if(ev.ctrlKey || ev.shiftKey) //-1 year
 +			{
 +				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() - 1)) ); // no need to catch dec -> jan for the year
 +				d = current.setFullYear( current.getFullYear() - 1 );
 +			}
 +			else
 +				d -= 604800000; // -7 days
 +		}
 +		else if (kc == Event.KEY_DOWN)
 +		{
 +			if(ev.ctrlKey || ev.shiftKey) // +1 year
 +			{
 +				current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year
 +				d = current.setFullYear( current.getFullYear() + 1 );
 +			}
 +			else
 +				d += 7 * 24 * 61 * 60 * 1000; // +7 days
 +		}
 +		this.setSelectedDate(d);
 +		Event.stop(ev);
 +	},
 +
 +	selectDate : function(ev)
 +	{
 +		var el = Event.element(ev);
 +		while (el.nodeType != 1)
 +			el = el.parentNode;
 +
 +		while (el != null && el.tagName && el.tagName.toLowerCase() != "td")
 +			el = el.parentNode;
 +
 +		// if no td found, return
 +		if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td")
 +			return;
 +
 +		var d = this.newDate(this.selectedDate);
 +		var n = Number(el.firstChild.data);
 +		if (isNaN(n) || n <= 0 || n == null)
 +			return;
 +
 +		d.setDate(n);
 +		this.setSelectedDate(d);
 +		this.hide();
 +	},
 +
 +	selectToday : function()
 +	{
 +		if(this.selectedDate.toISODate() == this.newDate().toISODate())
 +			this.hide();
 +
 +		this.setSelectedDate(this.newDate());
 +	},
 +
 +	clearSelection : function()
 +	{
 +		this.setSelectedDate(this.newDate());
 +		this.hide();
 +	},
 +
 +	monthSelect : function(ev)
 +	{
 +		this.setMonth(Form.Element.getValue(Event.element(ev)));
 +	},
 +
 +	yearSelect : function(ev)
 +	{
 +		this.setYear(Form.Element.getValue(Event.element(ev)));
 +	},
 +
 +	// ie6 extension
 +	mouseWheelChange : function (e)
 +	{
 +		if (e == null) e = document.parentWindow.event;
 +		var n = - e.wheelDelta / 120;
 +		var d = this.newDate(this.selectedDate);
 +		var m = d.getMonth() + n;
 +		this.setMonth(m);
 +
 +		return false;
 +	},
 +
 +	onChange : function()
 +	{
 +		if(this.options.InputMode == "TextBox")
 +		{
 +			this.control.value = this.formatDate();
 +			Event.fireEvent(this.control, "change");
 +		}
 +		else
 +		{
 +			var day = Prado.WebUI.TDatePicker.getDayListControl(this.control);
 +			var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
 +			var year = Prado.WebUI.TDatePicker.getYearListControl(this.control);
 +			var date = this.selectedDate;
 +			if(day)
 +			{
 +				day.selectedIndex = date.getDate()-1;
 +			}
 +			if(month)
 +			{
 +				month.selectedIndex = date.getMonth();
 +			}
 +			if(year)
 +			{
 +				var years = year.options;
 +				var currentYear = date.getFullYear();
 +				for(var i = 0; i < years.length; i++)
 +					years[i].selected = years[i].value.toInteger() == currentYear;
 +			}
 +			Event.fireEvent(day || month || year, "change");
 +		}
 +	},
 +
 +	formatDate : function()
 +	{
 +		return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : '';
 +	},
 +
 +	newDate : function(date)
 +	{
 +		if(!date)
 +			date = new Date();
 +		if(typeof(date) == "string" || typeof(date) == "number")
 +			date = new Date(date);
 +		return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0);
 +	},
 +
 +	setSelectedDate : function(date)
 +	{
 +		if (date == null)
 +			return;
 +		this.selectedDate = this.newDate(date);
 +
 +		this.updateHeader();
 +		this.update();
 +		if (typeof(this.onChange) == "function")
 +			this.onChange(this, date);
 +	},
 +
 +	getElement : function()
 +	{
 +		return this._calDiv;
 +	},
 +
 +	getSelectedDate : function ()
 +	{
 +		return this.selectedDate == null ? null : this.newDate(this.selectedDate);
 +	},
 +
 +	setYear : function(year)
 +	{
 +		var d = this.newDate(this.selectedDate);
 +		d.setFullYear(year);
 +		this.setSelectedDate(d);
 +	},
 +
 +	setMonth : function (month)
 +	{
 +		var d = this.newDate(this.selectedDate);
 +		d.setMonth(month);
 +		this.setSelectedDate(d);
 +	},
 +
 +	nextMonth : function ()
 +	{
 +		this.setMonth(this.selectedDate.getMonth()+1);
 +	},
 +
 +	prevMonth : function ()
 +	{
 +		this.setMonth(this.selectedDate.getMonth()-1);
 +	},
 +
 +	getDatePickerOffsetHeight : function()
 +	{
 +		if(this.options.InputMode == "TextBox")
 +			return this.control.offsetHeight;
 +
 +		var control = Prado.WebUI.TDatePicker.getDayListControl(this.control);
 +		if(control) return control.offsetHeight;
 +
 +		var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
 +		if(control) return control.offsetHeight;
 +
 +		var control = Prado.WebUI.TDatePicker.getYearListControl(this.control);
 +		if(control) return control.offsetHeight;
 +		return 0;
 +	},
 +
 +	show : function()
 +	{
 +		this.create();
 +
 +		if(!this.showing)
 +		{
 +			var pos = Position.positionedOffset(this.control);
 +
 +			pos[1] += this.getDatePickerOffsetHeight();
 +
 +			this._calDiv.style.display = "block";
 +			this._calDiv.style.top = (pos[1]-1) + "px";
 +			this._calDiv.style.left = pos[0] + "px";
 +
 +			this.ieHack(false);
 +			this.documentClickEvent = this.hideOnClick.bindEvent(this);
 +			this.documentKeyDownEvent = this.keyPressed.bindEvent(this);
 +			Event.observe(document.body, "click", this.documentClickEvent);
 +			var date = this.getDateFromInput();
 +			if(date)
 +			{
 +				this.selectedDate = date;
 +				this.setSelectedDate(date);
 +			}
 +			Event.observe(document,"keydown", this.documentKeyDownEvent);
 +			this.showing = true;
 +		}
 +	},
 +
 +	getDateFromInput : function()
 +	{
 +		if(this.options.InputMode == "TextBox")
 +			return Date.SimpleParse($F(this.control), this.Format);
 +		else
 +			return Prado.WebUI.TDatePicker.getDropDownDate(this.control);
 +	},
 +
 +	//hide the calendar when clicked outside any calendar
 +	hideOnClick : function(ev)
 +	{
 +		if(!this.showing) return;
 +		var el = Event.element(ev);
 +		var within = false;
 +		do
 +		{
 +			within = within || el.className == this.ClassName;
 +			within = within || el == this.trigger;
 +			within = within || el == this.control;
 +			if(within) break;
 +			el = el.parentNode;
 +		}
 +		while(el);
 +		if(!within) this.hide();
 +	},
 +
 +
 +	hide : function()
 +	{
 +		if(this.showing)
 +		{
 +			this._calDiv.style.display = "none";
 +			if(this.iePopUp)
 +				this.iePopUp.style.display = "none";
 +			this.showing = false;
 +			Event.stopObserving(document.body, "click", this.documentClickEvent);
 +			Event.stopObserving(document,"keydown", this.documentKeyDownEvent);
 +		}
 +	},
 +
 +	update : function()
 +	{
 +		// Calculate the number of days in the month for the selected date
 +		var date = this.selectedDate;
 +		var today = (this.newDate()).toISODate();
 +
 +		var selected = date.toISODate();
 +		var d1 = new Date(date.getFullYear(), date.getMonth(), 1);
 +		var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1);
 +		var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000));
 +
 +		// Find out the weekDay index for the first of this month
 +		var firstIndex = (d1.getDay() - this.FirstDayOfWeek) % 7 ;
 +	    if (firstIndex < 0)
 +	    	firstIndex += 7;
 +
 +		var index = 0;
 +		while (index < firstIndex) {
 +			this.dateSlot[index].value = -1;
 +			this.dateSlot[index].data.data = String.fromCharCode(160);
 +			this.dateSlot[index].data.parentNode.className = "empty";
 +			index++;
 +		}
 +
 +	    for (i = 1; i <= monthLength; i++, index++) {
 +			var slot = this.dateSlot[index];
 +			var slotNode = slot.data.parentNode;
 +			slot.value = i;
 +			slot.data.data = i;
 +			slotNode.className = "date";
 +			//slotNode.style.color = "";
 +			if (d1.toISODate() == today) {
 +				slotNode.className += " today";
 +			}
 +			if (d1.toISODate() == selected) {
 +			//	slotNode.style.color = "blue";
 +				slotNode.className += " selected";
 +			}
 +			d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1);
 +		}
 +
 +		var lastDateIndex = index;
 +
 +	    while(index < 42) {
 +			this.dateSlot[index].value = -1;
 +			this.dateSlot[index].data.data = String.fromCharCode(160);
 +			this.dateSlot[index].data.parentNode.className = "empty";
 +			++index;
 +		}
 +
 +	},
 +
 +	hover : function(ev)
 +	{
 +		if(Event.element(ev).tagName)
 +		{
 +			if(ev.type == "mouseover")
 +				Event.element(ev).addClassName("hover");
 +				else
 +				Event.element(ev).removeClassName("hover");
 +		}
 +	},
 +
 +	updateHeader : function () {
 +
 +		var options = this._monthSelect.options;
 +		var m = this.selectedDate.getMonth();
 +		for(var i=0; i < options.length; ++i) {
 +			options[i].selected = false;
 +			if (options[i].value == m) {
 +				options[i].selected = true;
 +			}
 +		}
 +
 +		options = this._yearSelect.options;
 +		var year = this.selectedDate.getFullYear();
 +		for(var i=0; i < options.length; ++i) {
 +			options[i].selected = false;
 +			if (options[i].value == year) {
 +				options[i].selected = true;
 +			}
 +		}
 +
 +	}
 +
 +
 +}; + diff --git a/framework/Web/Javascripts/js/debug/effects.js b/framework/Web/Javascripts/js/debug/effects.js new file mode 100644 index 00000000..7e8ba2ed --- /dev/null +++ b/framework/Web/Javascripts/js/debug/effects.js @@ -0,0 +1,960 @@ +// Copyright (c) 2005 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.   + +// converts rgb() and #xxx to #xxxxxx format,   +// returns self (or first argument) if not convertable   +String.prototype.parseColor = function() {   +  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);   +  } else {   +    if(this.slice(0,1) == '#') {   +      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();   +      if(this.length==7) color = this.toLowerCase();   +    }   +  }   +  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.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)) +      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; // 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; +} + +/* ------------- core effects ------------- */ + +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': +        // move unstarted effects after this effect   +        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { +            e.startOn  += effect.finishOn; +            e.finishOn += effect.finishOn; +          }); +        break; +      case 'end': +        // start effect after last queued effect has finished +        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.instances[queueName] = new Effect.ScopedQueue(); +       +    return this.instances[queueName]; +  } +} +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 +  sync:       false, // true for combining +  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); +    // make this work on IE on elements without 'layout' +    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() { +    // Bug in Opera: Opera returns the "real" position of a static element or +    // relative element that does not have top/left explicitly set. +    // ==> Always set top and left for position relative elements in your stylesheets  +    // (to 0 if you do not need them)  +    this.element.makePositioned(); +    this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); +    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0'); +    if(this.options.mode == 'absolute') { +      // absolute movement, so we need to calc deltaX and deltaY +      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' +    }); +  } +}); + +// for backwards compatibility +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',        // 'box' or 'contents' or {} with provided values +      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() { +    // 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'}); +    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'); +    // init color calculations +    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)); +  } +}); + +/* ------------- combination effects ------------- */ + +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, +  // force Safari to render floated elements properly +  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(); +  // SlideDown need to have the content of the element wrapped in a container element with fixed height! +  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();  +      // 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}); } +    }, 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] || {}) +  ); +} + +// 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); } +  }); +} + +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) +      ) +    } +  }); +} + +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(); + diff --git a/framework/Web/Javascripts/js/debug/logger.js b/framework/Web/Javascripts/js/debug/logger.js new file mode 100644 index 00000000..0609ec68 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/logger.js @@ -0,0 +1,731 @@ +/*
 +
 +Created By: Corey Johnson
 +E-mail: probablyCorey@gmail.com
 +
 +Requires: Prototype Javascript library (http://prototype.conio.net/)
 +
 +Use it all you want. Just remember to give me some credit :)
 +
 +*/
 +
 +// ------------
 +// Custom Event
 +// ------------
 +
 +CustomEvent = Class.create()
 +CustomEvent.prototype = {
 +  initialize : function() {
 +  	this.listeners = []
 +  },
 +
 +	addListener : function(method) {
 +		this.listeners.push(method)
 +	},
 +
 +	removeListener : function(method) {
 +		var foundIndexes = this._findListenerIndexes(method)
 +
 +		for(var i = 0; i < foundIndexes.length; i++) {
 +			this.listeners.splice(foundIndexes[i], 1)
 +		}
 +	},
 +
 +	dispatch : function(handler) {
 +		for(var i = 0; i < this.listeners.length; i++) {
 +			try {
 +				this.listeners[i](handler)
 +			}
 +			catch (e) {
 +				alert("Could not run the listener " + this.listeners[i] + ". " + e)
 +			}
 +		}
 +	},
 +
 +	// Private Methods
 +	// ---------------
 +	_findListenerIndexes : function(method) {
 +		var indexes = []
 +		for(var i = 0; i < this.listeners.length; i++) {			
 +			if (this.listeners[i] == method) {
 +				indexes.push(i)
 +			}
 +		}
 +
 +		return indexes
 +	}
 +}
 +
 +// ------
 +// Cookie
 +// ------
 +
 +var Cookie = {
 +	set : function(name, value, expirationInDays, path) {
 +		var cookie = escape(name) + "=" + escape(value)
 +
 +		if (expirationInDays) {
 +			var date = new Date()
 +			date.setDate(date.getDate() + expirationInDays)
 +			cookie += "; expires=" + date.toGMTString()
 +		} 
 +
 +		if (path) {
 +			cookie += ";path=" + path
 +		}
 +
 +		document.cookie = cookie
 +
 +		if (value && (expirationInDays == undefined || expirationInDays > 0) && !this.get(name)) {
 +			Logger.error("Cookie (" + name + ") was not set correctly... The value was " + value.toString().length + " charachters long (This may be over the cookie limit)");
 +		}
 +	},
 +
 +	get : function(name) {
 +		var pattern = "(^|;)\\s*" + escape(name) + "=([^;]+)"
 +
 +		var m = document.cookie.match(pattern)
 +		if (m && m[2]) {			
 +			return unescape(m[2])
 +		}
 +		else return null
 +	},
 +
 +	getAll : function() {
 +		var cookies = document.cookie.split(';')
 +		var cookieArray = []				
 +
 +		for (var i = 0; i < cookies.length; i++) {			
 +			try {
 +				var name = unescape(cookies[i].match(/^\s*([^=]+)/m)[1])
 +				var value = unescape(cookies[i].match(/=(.*$)/m)[1])
 +			}
 +			catch (e) {
 +				continue
 +			}
 +
 +			cookieArray.push({name : name, value : value})
 +
 +			if (cookieArray[name] != undefined) {
 +				Logger.waring("Trying to retrieve cookie named(" + name + "). There appears to be another property with this name though.");
 +			}			
 +
 +			cookieArray[name] = value
 +		}
 +
 +		return cookieArray
 +	},
 +
 +	clear : function(name) {
 +		this.set(name, "", -1)
 +	},
 +
 +	clearAll : function() {
 +		var cookies = this.getAll()
 +
 +		for(var i = 0; i < cookies.length; i++) {
 +			this.clear(cookies[i].name)
 +		}
 +
 +	}
 +}   
 +
 +// ------
 +// Logger
 +// -----        
 +
 +Logger = {
 +	logEntries : [],
 +
 +	onupdate : new CustomEvent(),
 +	onclear : new CustomEvent(),
 +    
 +
 +	// Logger output    
 +  log : function(message, tag) {
 +	  var logEntry = new LogEntry(message, tag || "info")
 +		this.logEntries.push(logEntry)
 +		this.onupdate.dispatch(logEntry)
 +	},
 +
 +	info : function(message) {
 +		this.log(message, 'info')
 +	}, 
 +
 +	debug : function(message) {
 +		this.log(message, 'debug')
 +	},  
 +
 +	warn : function(message) {
 +	  this.log(message, 'warning')
 +	},
 +
 +	error : function(message, error) {
 +	  this.log(message + ": \n" + error, 'error')
 +	},
 +
 +	clear : function () {
 +		this.logEntries = []
 +		this.onclear.dispatch()
 +	}
 +}  
 +
 +LogEntry = Class.create()
 +LogEntry.prototype = {  
 +    initialize : function(message, tag) {
 +      this.message = message
 +      this.tag = tag
 +    }
 +}
 +
 +LogConsole = Class.create()
 +LogConsole.prototype = {  
 +
 +  // Properties
 +  // ----------
 +  commandHistory : [],
 +  commandIndex : 0,
 +
 +  // Methods
 +  // -------
 +
 +  initialize : function() {
 +    this.outputCount = 0
 +    this.tagPattern = Cookie.get('tagPattern') || ".*"
 +  
 +  	// I hate writing javascript in HTML... but what's a better alternative
 +    this.logElement = document.createElement('div')
 +    document.body.appendChild(this.logElement)
 +    Element.hide(this.logElement)
 +
 +		this.logElement.style.position = "absolute"
 +    this.logElement.style.left = '0px'
 +    this.logElement.style.width = '100%'
 +
 +    this.logElement.style.textAlign = "left"
 +    this.logElement.style.fontFamily = "lucida console"
 +    this.logElement.style.fontSize = "100%"
 +    this.logElement.style.backgroundColor = 'darkgray'      
 +    this.logElement.style.opacity = 0.9 
 +    this.logElement.style.zIndex = 2000 
 +
 +    // Add toolbarElement
 +    this.toolbarElement = document.createElement('div')
 +    this.logElement.appendChild(this.toolbarElement)     
 +    this.toolbarElement.style.padding = "0 0 0 2px"
 +
 +    // Add buttons        
 +    this.buttonsContainerElement = document.createElement('span')
 +    this.toolbarElement.appendChild(this.buttonsContainerElement) 
 +
 +	this.buttonsContainerElement.innerHTML += '<button onclick="logConsole.toggle()" style="float:right;color:black">close</button>'
 +    this.buttonsContainerElement.innerHTML += '<button onclick="Logger.clear()" style="float:right;color:black">clear</button>'        
 +	if(!Prado.Inspector.disabled)
 +		this.buttonsContainerElement.innerHTML += '<button onclick="Prado.Inspector.inspect()" style="float:right;color:black; margin-right:15px;">Object Tree</button>'
 +
 +
 +		//Add Tag Filter
 +		this.tagFilterContainerElement = document.createElement('span')
 +    this.toolbarElement.appendChild(this.tagFilterContainerElement) 
 +    this.tagFilterContainerElement.style.cssFloat = 'left'
 +    this.tagFilterContainerElement.appendChild(document.createTextNode("Log Filter"))
 +    
 +    this.tagFilterElement = document.createElement('input')
 +    this.tagFilterContainerElement.appendChild(this.tagFilterElement)  
 +    this.tagFilterElement.style.width = '200px'                    
 +    this.tagFilterElement.value = this.tagPattern    
 +    this.tagFilterElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out
 +    
 +    Event.observe(this.tagFilterElement, 'keyup', this.updateTags.bind(this))
 +    Event.observe(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this))    
 +    
 +    // Add outputElement
 +    this.outputElement = document.createElement('div')
 +    this.logElement.appendChild(this.outputElement)  
 +    this.outputElement.style.overflow = "auto"              
 +    this.outputElement.style.clear = "both"
 +    this.outputElement.style.height = "200px"
 +    this.outputElement.style.backgroundColor = 'black' 
 +          
 +    this.inputContainerElement = document.createElement('div')
 +    this.inputContainerElement.style.width = "100%"
 +    this.logElement.appendChild(this.inputContainerElement)      
 +    
 +    this.inputElement = document.createElement('input')
 +    this.inputContainerElement.appendChild(this.inputElement)  
 +    this.inputElement.style.width = '100%'                    
 +    this.inputElement.style.borderWidth = '0px' // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0
 +    this.inputElement.style.margin = '0px'
 +    this.inputElement.style.padding = '0px'
 +    this.inputElement.value = 'Type command here' 
 +    this.inputElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out
 +
 +    Event.observe(this.inputElement, 'keyup', this.handleInput.bind(this))
 +    Event.observe(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this))    
 +
 +		window.setInterval(this.repositionWindow.bind(this), 500)
 +		this.repositionWindow()
 +		
 +    // Listen to the logger....
 +    Logger.onupdate.addListener(this.logUpdate.bind(this))
 +    Logger.onclear.addListener(this.clear.bind(this))		
 +
 +    // Preload log element with the log entries that have been entered
 +		for (var i = 0; i < Logger.logEntries.length; i++) {
 +  		this.logUpdate(Logger.logEntries[i])
 +  	}   
 +  	
 +  	// Feed all errors into the logger (For some unknown reason I can only get this to work
 +  	// with an inline event declaration)
 +  	Event.observe(window, 'error', function(msg, url, lineNumber) {Logger.error("Error in (" + (url || location) + ") on line "+lineNumber+"", msg)})
 +
 +    // Allow acess key link          
 +    var accessElement = document.createElement('span')
 +    accessElement.innerHTML = '<button style="position:absolute;top:-100px" onclick="javascript:logConsole.toggle()" accesskey="d"></button>'
 +  	document.body.appendChild(accessElement)
 +
 +  	if (Cookie.get('ConsoleVisible') == 'true') {
 +		  this.toggle()
 +		}
 +	},
 +
 +	toggle : function() {
 +	  if (this.logElement.style.display == 'none') {
 +		  this.show()
 +		}
 +		else {
 +			this.hide()
 +		}
 +	}, 
 +	
 +	show : function() {
 +	  Element.show(this.logElement)
 +	  this.outputElement.scrollTop = this.outputElement.scrollHeight // Scroll to bottom when toggled
 +	  Cookie.set('ConsoleVisible', 'true')
 + 	  this.inputElement.select()
 +	}, 
 +	
 +	hide : function() {
 +	  Element.hide(this.logElement)
 +	  Cookie.set('ConsoleVisible', 'false')
 +	},  
 +	
 +	output : function(message, style) {
 +			// If we are at the bottom of the window, then keep scrolling with the output			
 +			var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight
 +	
 +			this.outputCount++
 +	  	style = (style ? style += ';' : '')	  	
 +	  	style += 'padding:1px;margin:0 0 5px 0'	     
 +		  
 +		  if (this.outputCount % 2 == 0) style += ";background-color:#101010"
 +	  	
 +	  	message = message || "undefined"
 +	  	message = message.toString().escapeHTML()
 +	  	
 +	  	this.outputElement.innerHTML += "<pre style='" + style + "'>" + message + "</pre>"
 +	  	
 +	  	if (shouldScroll) {				
 +				this.outputElement.scrollTop = this.outputElement.scrollHeight
 +			}
 +	},
 +	
 +	updateTags : function() {
 +		var pattern = this.tagFilterElement.value
 +	
 +		if (this.tagPattern == pattern) return
 +
 +		try {
 +			new RegExp(pattern)
 +		}
 +		catch (e) {
 +			return
 +		}
 +		
 +		this.tagPattern = pattern
 +		Cookie.set('tagPattern', this.tagPattern)
 +
 +		this.outputElement.innerHTML = ""
 +		
 +		// Go through each log entry again
 +		this.outputCount = 0;
 +		for (var i = 0; i < Logger.logEntries.length; i++) {
 +  		this.logUpdate(Logger.logEntries[i])
 +  	}  
 +	},
 +	
 +	repositionWindow : function() {
 +		var offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
 +		var pageHeight = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
 +		this.logElement.style.top = (offset + pageHeight - Element.getHeight(this.logElement)) + "px"
 +	},
 +
 +	// Event Handlers
 +	// --------------
 +
 +	logUpdate : function(logEntry) {
 +		if (logEntry.tag.search(new RegExp(this.tagPattern, 'igm')) == -1) return
 +		var style = ''
 +	  if (logEntry.tag.search(/error/) != -1) style += 'color:red'
 +	  else if (logEntry.tag.search(/warning/) != -1) style += 'color:orange'
 +	  else if (logEntry.tag.search(/debug/) != -1) style += 'color:green'
 + 	  else if (logEntry.tag.search(/info/) != -1) style += 'color:white'
 +	  else style += 'color:yellow'
 +
 +		this.output(logEntry.message, style)
 +	},
 +
 +	clear : function(e) {
 +		this.outputElement.innerHTML = ""
 +	},
 +
 +	handleInput : function(e) {
 +		if (e.keyCode == Event.KEY_RETURN ) {      
 +	  	var command = this.inputElement.value
 +	  	
 +	  	switch(command) {
 +	    	case "clear":
 +	      		Logger.clear()  
 +	      		break
 +		    	
 +	    	default:        
 +	      	var consoleOutput = "" 
 +	      	
 +	      	try {
 +	        	consoleOutput = eval(this.inputElement.value)	        	
 +	      	}
 +	      	catch (e) {  
 +	        	Logger.error("Problem parsing input <" + command + ">", e)	        	
 +	        	break
 +					}
 +					
 +					Logger.log(consoleOutput)	      	
 +	      	break
 +			}        
 +	
 +	  	if (this.inputElement.value != "" && this.inputElement.value != this.commandHistory[0]) {
 +	    	this.commandHistory.unshift(this.inputElement.value)
 +	  	}
 +	  
 +	  	this.commandIndex = 0 
 +	  	this.inputElement.value = ""                                                        
 +		}
 +    else if (e.keyCode == Event.KEY_UP && this.commandHistory.length > 0) {
 +    	this.inputElement.value = this.commandHistory[this.commandIndex]
 +
 +			if (this.commandIndex < this.commandHistory.length - 1) {
 +      	this.commandIndex += 1
 +      }
 +    }     
 +    else if (e.keyCode == Event.KEY_DOWN && this.commandHistory.length > 0) {
 +    	if (this.commandIndex > 0) {                                      
 +      	this.commandIndex -= 1
 +	    }                       
 +
 +			this.inputElement.value = this.commandHistory[this.commandIndex]
 +	  } 
 + 		else {
 +    		this.commandIndex = 0
 +    }
 +	}  
 +}         
 +
 +// Load the Console when the window loads
 +var logConsole;
 +Event.OnLoad(function() { logConsole = new LogConsole()}); 
 +
 +
 +
 +
 +// -------------------------
 +// Helper Functions And Junk
 +// -------------------------
 +function inspect(o) 
 +{
 +	var objtype = typeof(o);
 +	if (objtype == "undefined") {
 +		return "undefined";
 +	} else if (objtype == "number" || objtype == "boolean") {
 +		return o + "";
 +	} else if (o === null) {
 +		return "null";
 +	}
 +
 +	 try {
 +            var ostring = (o + "");
 +        } catch (e) {
 +            return "[" + typeof(o) + "]";
 +        }
 +
 +	if (typeof(o) == "function") 
 +	{
 +            o = ostring.replace(/^\s+/, "");
 +            var idx = o.indexOf("{");
 +            if (idx != -1) {
 +                o = o.substr(0, idx) + "{...}";
 +            }
 +			return o;
 +       }
 +  
 +	var reprString = function (o) 
 +	{ 
 +		return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
 +			).replace(/[\f]/g, "\\f"
 +			).replace(/[\b]/g, "\\b"
 +			).replace(/[\n]/g, "\\n"
 +			).replace(/[\t]/g, "\\t"
 +			).replace(/[\r]/g, "\\r");
 +	};
 +
 +	if (objtype == "string") {
 +		return reprString(o);
 +	}
 +	// recurse
 +	var me = arguments.callee;
 +	// short-circuit for objects that support "json" serialization
 +	// if they return "self" then just pass-through...
 +	var newObj;
 +	if (typeof(o.__json__) == "function") {
 +		newObj = o.__json__();
 +		if (o !== newObj) {
 +			return me(newObj);
 +		}
 +	}
 +	if (typeof(o.json) == "function") {
 +		newObj = o.json();
 +		if (o !== newObj) {
 +			return me(newObj);
 +		}
 +	}
 +	// array
 +	if (objtype != "function" && typeof(o.length) == "number") {
 +		var res = [];
 +		for (var i = 0; i < o.length; i++) {
 +			var val = me(o[i]);
 +			if (typeof(val) != "string") {
 +				val = "undefined";
 +			}
 +			res.push(val);
 +		}
 +		return "[" + res.join(", ") + "]";
 +	}
 +   
 +	// generic object code path
 +	res = [];
 +	for (var k in o) {
 +		var useKey;
 +		if (typeof(k) == "number") {
 +			useKey = '"' + k + '"';
 +		} else if (typeof(k) == "string") {
 +			useKey = reprString(k);
 +		} else {
 +			// skip non-string or number keys
 +			continue;
 +		}
 +		val = me(o[k]);
 +		if (typeof(val) != "string") {
 +			// skip non-serializable values
 +			continue;
 +		}
 +		res.push(useKey + ":" + val);
 +	}
 +	return "{" + res.join(", ") + "}";
 +}   
 +
 +Array.prototype.contains = function(object) {
 +	for(var i = 0; i < this.length; i++) {
 +		if (object == this[i]) return true
 +	}
 +
 +	return false
 +}  
 +
 +// Helper Alias for simple logging
 +var puts = function() {return Logger.log(arguments[0], arguments[1])}      
 +
 +/*************************************
 +
 +	Javascript Object Tree
 +	version 1.0
 +	last revision:04.11.2004
 +	steve@slayeroffice.com
 +	http://slayeroffice.com
 +	
 +	(c)2004 S.G. Chipman
 +
 +	Please notify me of any modifications
 +	you make to this code so that I can 
 +	update the version hosted on slayeroffice.com
 +
 +
 +************************************/
 +if(typeof Prado == "undefined")
 +	var Prado = {};
 +Prado.Inspector = 
 +{
 +	d : document,	
 +	types : new Array(),
 +	objs : new Array(),
 +	hidden : new Array(),	
 +	opera : window.opera,
 +	displaying : '',
 +	nameList : new Array(),
 +
 +	format : function(str) {
 +		if(typeof(str) != "string") return str;
 +		str=str.replace(/</g,"<");
 +		str=str.replace(/>/g,">");
 +		return str;
 +	},
 +
 +	parseJS : function(obj) {
 +		var name;
 +		if(typeof obj == "string") {  name = obj; obj = eval(obj); }
 +		win= typeof obj == 'undefined' ? window : obj;
 +		this.displaying = name ? name : win.toString();
 +		for(js in win) {			
 +			try {
 +				if(win[js] && js.toString().indexOf("Inspector")==-1 && (win[js]+"").indexOf("[native code]")==-1) {
 +					
 +					t=typeof(win[js]);
 +					if(!this.objs[t.toString()]) {						
 +						this.types[this.types.length]=t;
 +						this.objs[t]={};
 +						this.nameList[t] = new Array();
 +					}
 +					this.nameList[t].push(js);
 +					this.objs[t][js] = this.format(win[js]+"");					
 +				}
 +			} catch(err) { }
 +		}
 +
 +		for(i=0;i<this.types.length;i++) 
 +			this.nameList[this.types[i]].sort();
 +	},
 +
 +	show : function(objID) {
 +		this.d.getElementById(objID).style.display=this.hidden[objID]?"none":"block";
 +		this.hidden[objID]=this.hidden[objID]?0:1;
 +	},
 +
 +	changeSpan : function(spanID) {
 +		if(this.d.getElementById(spanID).innerHTML.indexOf("+")>-1){
 +			this.d.getElementById(spanID).innerHTML="[-]";
 +		} else {
 +			this.d.getElementById(spanID).innerHTML="[+]";
 +		}
 +	},
 +
 +	buildInspectionLevel : function()
 +	{
 +		var display = this.displaying;
 +		var list = display.split(".");
 +		var links = ["<a href=\"javascript:var_dump()\">[object Window]</a>"];
 +		var name = '';
 +		if(display.indexOf("[object ") >= 0) return links.join(".");
 +		for(var i = 0; i < list.length; i++)
 +		{
 +			name += (name.length ? "." : "") + list[i];
 +			links[i+1] = "<a href=\"javascript:var_dump('"+name+"')\">"+list[i]+"</a>"; 
 +		}
 +		return links.join(".");
 +	},
 +
 +	buildTree : function() {
 +		mHTML = "<div>Inspecting "+this.buildInspectionLevel()+"</div>";
 +		mHTML +="<ul class=\"topLevel\">";
 +		this.types.sort();
 +		var so_objIndex=0;
 +		for(i=0;i<this.types.length;i++) 
 +		{
 +			mHTML+="<li style=\"cursor:pointer;\" onclick=\"Prado.Inspector.show('ul"+i+"');Prado.Inspector.changeSpan('sp" + i + "')\"><span id=\"sp" + i + "\">[+]</span><b>" + this.types[i] + "</b> (" + this.nameList[this.types[i]].length + ")</li><ul style=\"display:none;\" id=\"ul"+i+"\">";
 +			this.hidden["ul"+i]=0;
 +			for(e=0;e<this.nameList[this.types[i]].length;e++) 
 +			{
 +				var prop = this.nameList[this.types[i]][e];
 +				var value = this.objs[this.types[i]][prop]
 +				var more = "";
 +				if(value.indexOf("[object ") >= 0 && /^[a-zA-Z_]/.test(prop))
 +				{
 +					if(this.displaying.indexOf("[object ") < 0)
 +						more = " <a href=\"javascript:var_dump('"+this.displaying+"."+prop+"')\"><b>more</b></a>";
 +					else if(this.displaying.indexOf("[object Window]") >= 0)
 +						more = " <a href=\"javascript:var_dump('"+prop+"')\"><b>more</b></a>";	
 +				}
 +				mHTML+="<li style=\"cursor:pointer;\" onclick=\"Prado.Inspector.show('mul" + so_objIndex + "');Prado.Inspector.changeSpan('sk" + so_objIndex + "')\"><span id=\"sk" + so_objIndex + "\">[+]</span>" + prop + "</li><ul id=\"mul" + so_objIndex + "\" style=\"display:none;\"><li style=\"list-style-type:none;\"><pre>" + value + more + "</pre></li></ul>";
 +				this.hidden["mul"+so_objIndex]=0;
 +				so_objIndex++;
 +			}
 +			mHTML+="</ul>";
 +		}
 +		mHTML+="</ul>";
 +		this.d.getElementById("so_mContainer").innerHTML =mHTML;
 +	},
 +
 +	handleKeyEvent : function(e) {
 +		keyCode=document.all?window.event.keyCode:e.keyCode;
 +		if(keyCode==27) {
 +			this.cleanUp();
 +		}
 +	},
 +
 +	cleanUp : function() 
 +	{
 +		if(this.d.getElementById("so_mContainer"))
 +		{
 +			this.d.body.removeChild(this.d.getElementById("so_mContainer"));
 +			this.d.body.removeChild(this.d.getElementById("so_mStyle"));
 +			if(typeof Event != "undefined")
 +				Event.stopObserving(this.d, "keydown", this.dKeyDownEvent);
 +			this.types = new Array();
 +			this.objs = new Array();
 +			this.hidden = new Array();
 +		}
 +	},
 +
 +	disabled : document.all && !this.opera,
 +
 +	inspect : function(obj) 
 +	{
 +		if(this.disabled)return alert("Sorry, this only works in Mozilla and Firefox currently.");
 +		this.cleanUp();
 +		mObj=this.d.body.appendChild(this.d.createElement("div"));
 +		mObj.id="so_mContainer";
 +		sObj=this.d.body.appendChild(this.d.createElement("style"));
 +		sObj.id="so_mStyle";
 +		sObj.type="text/css";
 +		sObj.innerHTML = this.style;
 +		this.dKeyDownEvent = this.handleKeyEvent.bind(this);
 +		if(typeof Event != "undefined")
 +			Event.observe(this.d, "keydown", this.dKeyDownEvent);
 +
 +		this.parseJS(obj);
 +		this.buildTree();
 +
 +		cObj=mObj.appendChild(this.d.createElement("div"));
 +		cObj.className="credits";
 +		cObj.innerHTML = "<b>[esc] to <a href=\"javascript:Prado.Inspector.cleanUp();\">close</a></b><br />Javascript Object Tree V2.0.";
 +
 +		window.scrollTo(0,0);
 +	},
 +
 +	style : "#so_mContainer { position:absolute; top:5px; left:5px; background-color:#E3EBED; text-align:left; font:9pt verdana; width:85%; border:2px solid #000; padding:5px; z-index:1000;  color:#000; } " + 
 +			"#so_mContainer ul { padding-left:20px; } " + 
 +			"#so_mContainer ul li { display:block; list-style-type:none; list-style-image:url(); line-height:2em; -moz-border-radius:.75em; font:10px verdana; padding:0; margin:2px; color:#000; } " +
 +			"#so_mContainer li:hover { background-color:#E3EBED; } " + 
 +			"#so_mContainer ul li span { position:relative; width:15px; height:15px; margin-right:4px; } " +
 +			"#so_mContainer pre { background-color:#F9FAFB; border:1px solid #638DA1; height:auto; padding:5px; font:9px verdana; color:#000; } " + 
 +			"#so_mContainer .topLevel { margin:0; padding:0; } " +
 +			"#so_mContainer .credits { float:left; width:200px; font:6.5pt verdana; color:#000; padding:2px; margin-left:5px; text-align:left; border-top:1px solid #000; margin-top:15px; width:75%; } " +
 +			"#so_mContainer .credits a { font:9px verdana; font-weight:bold; color:#004465; text-decoration:none; background-color:transparent; }"
 +}
 +
 +//similar function to var_dump in PHP, brings up the javascript object tree UI.
 +function var_dump(obj)
 +{
 +	Prado.Inspector.inspect(obj);
 +}
 +
 +//similar function to print_r for PHP
 +var print_r = inspect; + diff --git a/framework/Web/Javascripts/js/debug/prado.js b/framework/Web/Javascripts/js/debug/prado.js new file mode 100644 index 00000000..9a610de9 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/prado.js @@ -0,0 +1,3086 @@ +/*  Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %> + *  (c) 2005 Sam Stephenson <sam@conio.net> + * + *  Prototype is freely distributable under the terms of an MIT-style license. + *  For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { +  Version: '1.50', +  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', +   +  emptyFunction: function() {}, +  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() {  +      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) {} +    } + +    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; +      } +    } +  } +} + + +
 +/**
 + * 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;
 +} + +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(); +    }); +  } +} + + +/**
 + * @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 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) { +      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); +  }, +   +  // removes whitespace-only text node children +  cleanWhitespace: function(element) { +    element = $(element); +    for (var i = 0; i < element.childNodes.length; i++) { +      var node = element.childNodes[i]; +      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))  +        Element.remove(node); +    } +  }, +   +  empty: function(element) { +    return $(element).innerHTML.match(/^\s*$/); +  }, +   +  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}; +     +    // All *Width and *Height properties give 0 on elements with display none, +    // so enable the element temporarily +    var els = element.style; +    var originalVisibility = els.visibility; +    var originalPosition = els.position; +    els.visibility = 'hidden'; +    els.position = 'absolute'; +    els.display = ''; +    var originalWidth = element.clientWidth; +    var originalHeight = element.clientHeight; +    els.display = 'none'; +    els.position = originalPosition; +    els.visibility = originalVisibility; +    return {width: originalWidth, height: originalHeight};     +  }, +   +  makePositioned: function(element) { +    element = $(element); +    var pos = Element.getStyle(element, 'position'); +    if (pos == 'static' || !pos) { +      element._madePositioned = true; +      element.style.position = 'relative'; +      // Opera returns the offset relative to the positioning context, when an +      // element is position relative but top and left have not been defined +      if (window.opera) { +        element.style.top = 0; +        element.style.left = 0; +      }   +    } +  }, + +  undoPositioned: function(element) { +    element = $(element); +    if (element._madePositioned) { +      element._madePositioned = undefined; +      element.style.position = +        element.style.top = +        element.style.left = +        element.style.bottom = +        element.style.right = '';    +    } +  }, + +  makeClipping: function(element) { +    element = $(element); +    if (element._overflow) return; +    element._overflow = element.style.overflow; +    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') +      element.style.overflow = 'hidden'; +  }, + +  undoClipping: function(element) { +    element = $(element); +    if (element._overflow) return; +    element.style.overflow = element._overflow; +    element._overflow = undefined; +  } +} + +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) { +    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; +    } +  }, + +  // find the first node with the given tagName, starting from the +  // node the event was triggered on; traverses the DOM upwards +  findElement: function(event, tagName) { +    var element = Event.element(event); +    while (element.parentNode && (!element.tagName || +        (element.tagName.toUpperCase() != tagName.toUpperCase()))) +      element = element.parentNode; +    return element; +  }, + +  observers: false, +   +  _observeAndCache: function(element, name, observer, useCapture) { +    if (!this.observers) this.observers = []; +    if (element.addEventListener) { +      this.observers.push([element, name, observer, useCapture]); +      element.addEventListener(name, observer, useCapture); +    } else if (element.attachEvent) { +      this.observers.push([element, name, observer, useCapture]); +      element.attachEvent('on' + name, observer); +    } +  }, +   +  unloadCache: function() { +    if (!Event.observers) return; +    for (var i = 0; i < Event.observers.length; i++) { +      Event.stopObserving.apply(this, Event.observers[i]); +      Event.observers[i][0] = null; +    } +    Event.observers = false; +  }, + +  observe: function(element, name, observer, useCapture) { +    var element = $(element); +    useCapture = useCapture || false; +     +    if (name == 'keypress' && +        (navigator.appVersion.match(/Konqueror|Safari|KHTML/) +        || element.attachEvent)) +      name = 'keydown'; +     +    this._observeAndCache(element, name, observer, useCapture); +  }, + +  stopObserving: function(element, name, observer, useCapture) { +    var element = $(element); +    useCapture = useCapture || false; +     +    if (name == 'keypress' && +        (navigator.appVersion.match(/Konqueror|Safari|KHTML/) +        || element.detachEvent)) +      name = 'keydown'; +     +    if (element.removeEventListener) { +      element.removeEventListener(name, observer, useCapture); +    } else if (element.detachEvent) { +      element.detachEvent('on' + name, observer); +    } +  } +}); + +/* prevent memory leaks in IE */ +if (navigator.appVersion.match(/\bMSIE\b/)) +  Event.observe(window, 'unload', Event.unloadCache, 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)
 +	{
 +		element = $(element);
 +		if(type == "submit")
 +			return element.submit();
 +		if(document.createEvent)
 +        {            
 +			if(Event.isHTMLEvent(type))
 +			{
 +				var event = document.createEvent('HTMLEvents');
 +	            event.initEvent(type, true, true);
 +			}
 +			else if(Event.isMouseEvent(type))
 +			{
 +				var event = document.createEvent('MouseEvents');
 +				event.initMouseEvent(type,true,true,
 +					document.defaultView, 1, 0, 0, 0, 0, false, 
 +							false, false, false, 0, null);
 +			}
 +            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; +  }, + +  realOffset: function(element) { +    var valueT = 0, valueL = 0; +    do { +      valueT += element.scrollTop  || 0; +      valueL += element.scrollLeft || 0;  +      element = element.parentNode; +    } while (element); +    return [valueL, valueT]; +  }, + +  cumulativeOffset: function(element) { +    var valueT = 0, valueL = 0; +    do { +      valueT += element.offsetTop  || 0; +      valueL += element.offsetLeft || 0; +      element = element.offsetParent; +    } while (element); +    return [valueL, valueT]; +  }, + +  positionedOffset: function(element) { +    var valueT = 0, valueL = 0; +    do { +      valueT += element.offsetTop  || 0; +      valueL += element.offsetLeft || 0; +      element = element.offsetParent; +      if (element) { +        p = Element.getStyle(element, 'position'); +        if (p == 'relative' || p == 'absolute') break; +      } +    } while (element); +    return [valueL, valueT]; +  }, +   +  offsetParent: function(element) { +    if (element.offsetParent) return element.offsetParent; +    if (element == document.body) return element; + +    while ((element = element.parentNode) && element != document.body) +      if (Element.getStyle(element, 'position') != 'static') +        return element; + +    return document.body; +  }, +   +  // caches x/y coordinate pair to use with overlap +  within: function(element, x, y) { +    if (this.includeScrollOffsets) +      return this.withinIncludingScrolloffsets(element, x, y); +    this.xcomp = x; +    this.ycomp = y; +    this.offset = this.cumulativeOffset(element); + +    return (y >= this.offset[1] && +            y <  this.offset[1] + element.offsetHeight && +            x >= this.offset[0] &&  +            x <  this.offset[0] + element.offsetWidth); +  }, + +  withinIncludingScrolloffsets: function(element, x, y) { +    var offsetcache = this.realOffset(element); + +    this.xcomp = x + offsetcache[0] - this.deltaX; +    this.ycomp = y + offsetcache[1] - this.deltaY; +    this.offset = this.cumulativeOffset(element); + +    return (this.ycomp >= this.offset[1] && +            this.ycomp <  this.offset[1] + element.offsetHeight && +            this.xcomp >= this.offset[0] &&  +            this.xcomp <  this.offset[0] + element.offsetWidth); +  }, + +  // within must be called directly before +  overlap: function(mode, element) {   +    if (!mode) return 0;   +    if (mode == 'vertical')  +      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /  +        element.offsetHeight; +    if (mode == 'horizontal') +      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /  +        element.offsetWidth; +  }, + +  clone: function(source, target) { +    source = $(source); +    target = $(target); +    target.style.position = 'absolute'; +    var offsets = this.cumulativeOffset(source); +    target.style.top    = offsets[1] + 'px'; +    target.style.left   = offsets[0] + 'px'; +    target.style.width  = source.offsetWidth + 'px'; +    target.style.height = source.offsetHeight + 'px'; +  }, + +  page: function(forElement) { +    var valueT = 0, valueL = 0; + +    var element = forElement; +    do { +      valueT += element.offsetTop  || 0; +      valueL += element.offsetLeft || 0; + +      // Safari fix +      if (element.offsetParent==document.body) +        if (Element.getStyle(element,'position')=='absolute') break; + +    } while (element = element.offsetParent); + +    element = forElement; +    do { +      valueT -= element.scrollTop  || 0; +      valueL -= element.scrollLeft || 0;     +    } while (element = element.parentNode); + +    return [valueL, valueT]; +  }, + +  clone: function(source, target) { +    var options = Object.extend({ +      setLeft:    true, +      setTop:     true, +      setWidth:   true, +      setHeight:  true, +      offsetTop:  0, +      offsetLeft: 0 +    }, arguments[2] || {}) + +    // find page position of source +    source = $(source); +    var p = Position.page(source); + +    // find coordinate system to use +    target = $(target); +    var delta = [0, 0]; +    var parent = null; +    // delta [0,0] will do fine with position: fixed elements,  +    // position:absolute needs offsetParent deltas +    if (Element.getStyle(target,'position') == 'absolute') { +      parent = Position.offsetParent(target); +      delta = Position.page(parent); +    } + +    // correct by body offsets (fixes Safari) +    if (parent == document.body) { +      delta[0] -= document.body.offsetLeft; +      delta[1] -= document.body.offsetTop;  +    } + +    // set position +    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px'; +    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px'; +    if(options.setWidth)  target.style.width = source.offsetWidth + 'px'; +    if(options.setHeight) target.style.height = source.offsetHeight + 'px'; +  }, + +  absolutize: function(element) { +    element = $(element); +    if (element.style.position == 'absolute') return; +    Position.prepare(); + +    var offsets = Position.positionedOffset(element); +    var top     = offsets[1]; +    var left    = offsets[0]; +    var width   = element.clientWidth; +    var height  = element.clientHeight; + +    element._originalLeft   = left - parseFloat(element.style.left  || 0); +    element._originalTop    = top  - parseFloat(element.style.top || 0); +    element._originalWidth  = element.style.width; +    element._originalHeight = element.style.height; + +    element.style.position = 'absolute'; +    element.style.top    = top + 'px';; +    element.style.left   = left + 'px';; +    element.style.width  = width + 'px';; +    element.style.height = height + 'px';; +  }, + +  relativize: function(element) { +    element = $(element); +    if (element.style.position == 'relative') return; +    Position.prepare(); + +    element.style.position = 'relative'; +    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0); +    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + +    element.style.top    = top + 'px'; +    element.style.left   = left + 'px'; +    element.style.height = element._originalHeight; +    element.style.width  = element._originalWidth; +  } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned.  For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { +  Position.cumulativeOffset = function(element) { +    var valueT = 0, valueL = 0; +    do { +      valueT += element.offsetTop  || 0; +      valueL += element.offsetLeft || 0; +      if (element.offsetParent == document.body) +        if (Element.getStyle(element, 'position') == 'absolute') break; +         +      element = element.offsetParent; +    } while (element); +     +    return [valueL, valueT]; +  } +} + + + + +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(); +} + + +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// See scriptaculous.js for full license. + +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' +  }, +  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, +  //       due to a Firefox bug +  node: function(elementName) { +    elementName = elementName.toUpperCase(); +     +    // try innerHTML approach +    var parentTag = this.NODEMAP[elementName] || 'div'; +    var parentElement = document.createElement(parentTag); +    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 +      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">"; +    } catch(e) {} +    var element = parentElement.firstChild || null; +       +    // see if browser added wrapping tags +    if(element && (element.tagName != elementName)) +      element = element.getElementsByTagName(elementName)[0]; +     +    // fallback to createElement approach +    if(!element) element = document.createElement(elementName); +     +    // abort if nothing could be created +    if(!element) return; + +    // attributes (or text) +    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 { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 +              parentElement.innerHTML = "<" +elementName + " " + +                attrs + "></" + elementName + ">"; +            } catch(e) {} +            element = parentElement.firstChild || null; +            // workaround firefox 1.0.X bug +            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]; +            } +        }  + +    // text, or array of children +    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') { // array can hold nodes and text +      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)
 +	{
 +		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);
 +		
 +		// do some funky regexs to replace the format string
 +		// with the real values
 +		var frm = new String(format);
 +		for (var sect in bits) 
 +		{
 +			var reg = new RegExp("\\b"+sect+"\\b" ,"g");
 +			frm = frm.replace(reg, bits[sect]);
 +		}
 +		return frm;
 +	},
 +
 +	toISODate : function()
 +	{
 +		var y = this.getFullYear();
 +		var m = String(this.getMonth() + 1).zerofill(2);
 +		var d = String(this.getDate()).zerofill(2);
 +		return String(y) + String(m) + String(d);
 +	}
 +});
 +
 +Object.extend(Date, 
 +{
 +	SimpleParse: function(value, format)
 +	{	
 +		val=String(value);
 +		format=String(format);
 +		
 +		if(val.length <= 0) return null;
 +		
 +		if(format.length <= 0) return new Date(value);
 +			
 +		var isInteger = function (val) 
 +		{
 +			var digits="1234567890";
 +			for (var i=0; i < val.length; i++) 
 +			{
 +				if (digits.indexOf(val.charAt(i))==-1) { return false; }
 +			}
 +			return true;
 +		};
 +		
 +		var getInt = function(str,i,minlength,maxlength) 
 +		{
 +			for (var x=maxlength; x>=minlength; x--) 
 +			{
 +				var token=str.substring(i,i+x);
 +				if (token.length < minlength) { return null; }
 +				if (isInteger(token)) { return token; }
 +			}
 +			return null;
 +		};
 +	
 +		var i_val=0;
 +		var i_format=0;
 +		var c="";
 +		var token="";
 +		var token2="";
 +		var x,y;
 +		var now=new Date();
 +		var year=now.getFullYear();
 +		var month=now.getMonth()+1;
 +		var date=1;
 +	
 +		while (i_format < format.length) 
 +		{
 +			// Get next token from format string
 +			c=format.charAt(i_format);
 +			token="";
 +			while ((format.charAt(i_format)==c) && (i_format < format.length)) 
 +			{
 +				token += format.charAt(i_format++);
 +			}
 +		
 +			// Extract contents of value based on format token
 +			if (token=="yyyy" || token=="yy" || token=="y") 
 +			{
 +				if (token=="yyyy") { x=4;y=4; }
 +				if (token=="yy")   { x=2;y=2; }
 +				if (token=="y")    { x=2;y=4; }
 +				year=getInt(val,i_val,x,y);
 +				if (year==null) { return null; }
 +				i_val += year.length;
 +				if (year.length==2) 
 +				{
 +					if (year > 70) { year=1900+(year-0); }
 +					else { year=2000+(year-0); }
 +				}
 +			}
 +
 +			else if (token=="MM"||token=="M") 
 +			{
 +				month=getInt(val,i_val,token.length,2);
 +				if(month==null||(month<1)||(month>12)){return null;}
 +				i_val+=month.length;
 +			}
 +			else if (token=="dd"||token=="d") 
 +			{
 +				date=getInt(val,i_val,token.length,2);
 +				if(date==null||(date<1)||(date>31)){return null;}
 +				i_val+=date.length;
 +			}
 +			else 
 +			{
 +				if (val.substring(i_val,i_val+token.length)!=token) {return null;}
 +				else {i_val+=token.length;}
 +			}
 +		}
 +	
 +		// If there are any trailing characters left in the value, it doesn't match
 +		if (i_val != val.length) { return null; }
 +		
 +		// Is date valid for month?
 +		if (month==2) 
 +		{
 +			// Check for leap year
 +			if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
 +				if (date > 29){ return null; }
 +			}
 +			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.0.0',
 +
 +	/**
 +	 * Returns browser information. Example
 +	 * <code>
 +	 * var browser = Prado.Browser();
 +	 * alert(browser.ie); //should ouput true if IE, false otherwise
 +	 * </code>
 +	 * @param ${parameter}
 +	 * @return ${return}
 +	 */
 +	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
 +		{
 +			var elm = doc.createElement("link");
 +
 +			elm.rel = "stylesheet";
 +			elm.href = css_file;
 +
 +			if (headArr = doc.getElementsByTagName("head"))
 +				headArr[0].appendChild(elm);
 +		}
 +	}
 +};
 + + +/*Prado.Focus = Class.create();
 +
 +Prado.Focus.setFocus = function(id)
 +{
 +	var target = document.getElementById ? document.getElementById(id) : document.all[id];
 +	if(target && !Prado.Focus.canFocusOn(target))
 +	{
 +		target = Prado.Focus.findTarget(target);
 +	}
 +	if(target)
 +	{
 +        try
 +		{
 +            target.focus();
 +			target.scrollIntoView(false);
 +            if (window.__smartNav)
 +			{
 +				window.__smartNav.ae = target.id;
 +			}
 +		}
 +        catch (e)
 +		{
 +		}
 +	}
 +}
 +
 +Prado.Focus.canFocusOn = function(element)
 +{
 +	if(!element || !(element.tagName))
 +		return false;
 +	var tagName = element.tagName.toLowerCase();
 +	return !element.disabled && (!element.type || element.type.toLowerCase() != "hidden") && Prado.Focus.isFocusableTag(tagName) && Prado.Focus.isVisible(element);
 +}
 +
 +Prado.Focus.isFocusableTag = function(tagName)
 +{
 +	return (tagName == "input" || tagName == "textarea" || tagName == "select" || tagName == "button" || tagName == "a");
 +}
 +
 +
 +Prado.Focus.findTarget = function(element)
 +{
 +	if(!element || !(element.tagName))
 +	{
 +		return null;
 +	}
 +	var tagName = element.tagName.toLowerCase();
 +	if (tagName == "undefined")
 +	{
 +		return null;
 +	}
 +	var children = element.childNodes;
 +	if (children)
 +	{
 +		for(var i=0;i<children.length;i++)
 +		{
 +			try
 +			{
 +				if(Prado.Focus.canFocusOn(children[i]))
 +				{
 +					return children[i];
 +				}
 +				else
 +				{
 +					var target = Prado.Focus.findTarget(children[i]);
 +					if(target)
 +					{
 +						return target;
 +					}
 +				}
 +			}
 +			catch (e)
 +			{
 +			}
 +		}
 +	}
 +	return null;
 +}
 +
 +Prado.Focus.isVisible = function(element)
 +{
 +	var current = element;
 +	while((typeof(current) != "undefined") && (current != null))
 +	{
 +		if(current.disabled || (typeof(current.style) != "undefined" && ((typeof(current.style.display) != "undefined" && current.style.display == "none") || (typeof(current.style.visibility) != "undefined" && current.style.visibility == "hidden") )))
 +		{
 +			return false;
 +		}
 +		if(typeof(current.parentNode) != "undefined" &&	current.parentNode != null && current.parentNode != current && current.parentNode.tagName.toLowerCase() != "body")
 +		{
 +			current = current.parentNode;
 +		}
 +		else
 +		{
 +			return true;
 +		}
 +	}
 +    return true;
 +}
 +*/
 +
 +
 +Prado.PostBack = function(event,options)
 +{
 +	var form = $(options['FormID']);
 +	var canSubmit = true;
 +
 +	if(options['CausesValidation'] && typeof(Prado.Validation) != "undefined")
 +	{
 +		if(!Prado.Validation.validate(options['FormID'], options['ValidationGroup'], $(options['ID'])))
 +			return Event.stop(event);
 +	}
 +
 +	if(options['PostBackUrl'] && options['PostBackUrl'].length > 0)
 +		form.action = options['PostBackUrl'];
 +
 +	if(options['TrackFocus'])
 +	{
 +		var lastFocus = $('PRADO_LASTFOCUS');
 +		if(lastFocus)
 +		{
 +			var active = document.activeElement; //where did this come from
 +			if(active)
 +				lastFocus.value = active.id;
 +			else
 +				lastFocus.value = options['EventTarget'];
 +		}
 +	}
 +
 +	$('PRADO_POSTBACK_TARGET').value = options['EventTarget'];
 +	$('PRADO_POSTBACK_PARAMETER').value = options['EventParameter'];
 +	/**
 +	 * Since google toolbar prevents browser default action,
 +	 * we will always disable default client-side browser action
 +	 */
 +	/*if(options['StopEvent']) */
 +		Event.stop(event);
 +	Event.fireEvent(form,"submit");
 +}
 +
 +/*
 +
 +Prado.doPostBack = function(formID, eventTarget, eventParameter, performValidation, validationGroup, actionUrl, trackFocus, clientSubmit)
 +{
 +	if (typeof(performValidation) == 'undefined')
 +	{
 +		var performValidation = false;
 +		var validationGroup = '';
 +		var actionUrl = null;
 +		var trackFocus = false;
 +		var clientSubmit = true;
 +	}
 +	var theForm = document.getElementById ? document.getElementById(formID) : document.forms[formID];
 +	var canSubmit = true;
 +    if (performValidation)
 +	{
 +		//canSubmit = Prado.Validation.validate(validationGroup);
 +	*	Prado.Validation.ActiveTarget = theForm;
 +		Prado.Validation.CurrentTargetGroup = null;
 +		Prado.Validation.IsGroupValidation = false;
 +		canSubmit = Prado.Validation.IsValid(theForm);
 +		Logger.debug(canSubmit);*
 +		canSubmit = Prado.Validation.IsValid(theForm);
 +	}
 +	if (canSubmit)
 +	{
 +		if (actionUrl != null && (actionUrl.length > 0))
 +		{
 +			theForm.action = actionUrl;
 +		}
 +		if (trackFocus)
 +		{
 +			var lastFocus = theForm.elements['PRADO_LASTFOCUS'];
 +			if ((typeof(lastFocus) != 'undefined') && (lastFocus != null))
 +			{
 +				var active = document.activeElement;
 +				if (typeof(active) == 'undefined')
 +				{
 +					lastFocus.value = eventTarget;
 +				}
 +				else
 +				{
 +					if ((active != null) && (typeof(active.id) != 'undefined'))
 +					{
 +						if (active.id.length > 0)
 +						{
 +							lastFocus.value = active.id;
 +						}
 +						else if (typeof(active.name) != 'undefined')
 +						{
 +							lastFocus.value = active.name;
 +						}
 +					}
 +				}
 +			}
 +		}
 +		if (!clientSubmit)
 +		{
 +			canSubmit = false;
 +		}
 +	}
 +	if (canSubmit && (!theForm.onsubmit || theForm.onsubmit()))
 +	{
 +		theForm.PRADO_POSTBACK_TARGET.value = eventTarget;
 +		theForm.PRADO_POSTBACK_PARAMETER.value = eventParameter;
 +		theForm.submit();
 +	}
 +}
 +*/ + +Prado.Element = 
 +{
 +	/**
 +	 * Set the value of a particular element.
 +	 * @param string element id
 +	 * @param string new element value.
 +	 */
 +	setValue : function(element, value)
 +	{
 +		var el = $(element);
 +		if(el && typeof(el.value) != "undefined")
 +			el.value = value;
 +	},
 +
 +	select : function(element, method, value)
 +	{
 +		var el = $(element);
 +		var isList = element.indexOf('[]') > -1;
 +		if(!el && !isList) return;
 +		method = isList ? 'check'+method : el.tagName.toLowerCase()+method;
 +		var selection = Prado.Element.Selection;
 +		if(isFunction(selection[method])) 
 +			selection[method](isList ? element : el,value);
 +	},
 +
 +	click : function(element)
 +	{
 +		var el = $(element);
 +		if(el) 
 +			Event.fireEvent(el,'click');
 +	},
 +	
 +	setAttribute : function(element, attribute, value)
 +	{
 +		var el = $(element);
 +		if(attribute == "disabled" && value==false)
 +			el.removeAttribute(attribute);
 +		else
 +			el.setAttribute(attribute, value);
 +	},
 +
 +	setOptions : function(element, options)
 +	{
 +		var el = $(element);
 +		if(el && el.tagName.toLowerCase() == "select")
 +		{
 +			while(el.length > 0)
 +				el.remove(0);
 +			for(var i = 0; i<options.length; i++)
 +				el.options[el.options.length] = new Option(options[i][0],options[i][1]);
 +		}
 +	},
 +
 +	/**
 +	 * A delayed focus on a particular element
 +	 * @param {element} element to apply focus()
 +	 */
 +	focus : function(element)
 +	{
 +		var obj = $(element);
 +		if(typeof(obj) != "undefined" && typeof(obj.focus) != "undefined")
 +			setTimeout(function(){ obj.focus(); }, 100);
 +		return false;
 +	}
 +}
 +
 +Prado.Element.Selection = 
 +{
 +	inputValue : function(el, value)
 +	{
 +		switch(el.type.toLowerCase()) 
 +		{
 +			case 'checkbox':  
 +			case 'radio':
 +			return el.checked = value;
 +		}
 +	},
 +
 +	selectValue : function(el, value)
 +	{
 +		$A(el.options).each(function(option)
 +		{
 +			option.selected = option.value == value;
 +		});
 +	},
 +
 +	selectIndex : function(el, index)
 +	{
 +		if(el.type == 'select-one')
 +			el.selectedIndex = index;
 +		else
 +		{
 +			for(var i = 0; i<el.length; i++)
 +			{
 +				if(i == index)
 +					el.options[i].selected = true;
 +			}
 +		}
 +	},
 +
 +	selectClear : function(el)
 +	{
 +		el.selectedIndex = -1;
 +	},
 +
 +	selectAll : function(el)
 +	{
 +		$A(el.options).each(function(option)
 +		{
 +			option.selected = true;
 +			Logger.warn(option.value);
 +		});
 +	},
 +
 +	selectInvert : function(el)
 +	{
 +		$A(el.options).each(function(option)
 +		{
 +			option.selected = !option.selected;
 +		});
 +	},
 +
 +	checkValue : function(name, value)
 +	{
 +		$A(document.getElementsByName(name)).each(function(el)
 +		{
 +			el.checked = el.value == value
 +		});
 +	},
 +
 +	checkIndex : function(name, index)
 +	{
 +		var elements = $A(document.getElementsByName(name));
 +		for(var i = 0; i<elements.length; i++)
 +		{
 +			if(i == index)
 +				elements[i].checked = true;
 +		}
 +	},
 +
 +	checkClear : function(name)
 +	{
 +		$A(document.getElementsByName(name)).each(function(el)
 +		{
 +			el.checked = false;
 +		});
 +	},
 +
 +	checkAll : function(name)
 +	{
 +		$A(document.getElementsByName(name)).each(function(el)
 +		{
 +			el.checked = true;
 +		});
 +	},
 +	checkInvert : function(name)
 +	{
 +		$A(document.getElementsByName(name)).each(function(el)
 +		{
 +			el.checked = !el.checked;
 +		});
 +	}
 +}; + +Prado.WebUI = Class.create();
 +
 +//base postback-able controls
 +/*Prado.WebUI.PostBackControl = Class.create();
 +Prado.WebUI.PostBackControl.prototype =
 +{
 +	initialize : function(options)
 +	{
 +		this.element = $(options['ID']);
 +		
 +/*		if(options.CausesValidation && typeof(Prado.Validation) != 'undefined')
 +		{
 +			Prado.Validation.registerTarget(options);
 +		}
 +		
 +		//TODO: what do the following options do?
 +		//options['PostBackUrl']
 +		//options['ClientSubmit']
 +
 +		if(this.onInit)
 +			this.onInit(options);
 +	}
 +};
 +
 +//short cut to create postback components
 +Prado.WebUI.createPostBackComponent = function(definition)
 +{
 +	var component = Class.create();
 +	Object.extend(component.prototype, Prado.WebUI.PostBackControl.prototype);
 +	if(definition) Object.extend(component.prototype, definition);
 +	return component;
 +}
 +
 +Prado.WebUI.TButton = Prado.WebUI.createPostBackComponent();
 +*/
 +Prado.WebUI.PostBackControl = Class.create();
 +
 +Prado.WebUI.PostBackControl.prototype = 
 +{
 +	_elementOnClick : null, //capture the element's onclick function
 +
 +	initialize : function(options)
 +	{
 +		this.element = $(options.ID);
 +		if(this.onInit)
 +			this.onInit(options);
 +	},
 +	
 +	onInit : function(options)
 +	{
 +		if(typeof(this.element.onclick)=="function")
 +		{
 +			this._elementOnClick = this.element.onclick;
 +			this.element.onclick = null;
 +		}
 +		Event.observe(this.element, "click", this.onClick.bindEvent(this,options));		
 +	},
 +
 +	onClick : function(event, options)
 +	{
 +		var src = Event.element(event);
 +		var doPostBack = true;
 +		var onclicked = null;
 +
 +		if(this._elementOnClick)
 +		{
 +			var onclicked = this._elementOnClick(event);
 +			if(typeof(onclicked) == "boolean")
 +				doPostBack = onclicked;
 +		}
 +		if(doPostBack)
 +			this.onPostBack(event,options);
 +		if(typeof(onclicked) == "boolean" && !onclicked)
 +			Event.stop(event);
 +	},
 +
 +	onPostBack : function(event, options)
 +	{
 +		Prado.PostBack(event,options);
 +	}
 +};
 +
 +Prado.WebUI.TButton = Class.extend(Prado.WebUI.PostBackControl);
 +Prado.WebUI.TLinkButton = Class.extend(Prado.WebUI.PostBackControl);
 +Prado.WebUI.TCheckBox = Class.extend(Prado.WebUI.PostBackControl);
 +Prado.WebUI.TBulletedList = Class.extend(Prado.WebUI.PostBackControl);
 +Prado.WebUI.TImageMap = Class.extend(Prado.WebUI.PostBackControl);
 +
 +/**
 + * TImageButton client-side behaviour. With validation, Firefox needs 
 + * to capture the x,y point of the clicked image in hidden form fields.
 + */
 +Prado.WebUI.TImageButton = Class.extend(Prado.WebUI.PostBackControl);
 +Object.extend(Prado.WebUI.TImageButton.prototype,
 +{
 +	/**
 +	 * Only add the hidden inputs once.
 +	 */
 +	hasXYInput : false,
 +	
 +	/**
 +	 * Override parent onPostBack function, tried to add hidden forms
 +	 * inputs to capture x,y clicked point.
 +	 */
 +	onPostBack : function(event, options)
 +	{
 +		if(!this.hasXYInput)
 +		{
 +			this.addXYInput(event,options);
 +			this.hasXYInput = true;
 +		}
 +		Prado.PostBack(event, options);
 +	},
 +	
 +	/**
 +	 * Add hidden inputs to capture the x,y point clicked on the image.
 +	 * @param event DOM click event.
 +	 * @param array image button options.
 +	 */
 +	addXYInput : function(event,options)
 +	{
 +		var imagePos = Position.cumulativeOffset(this.element);
 +		var clickedPos = [event.clientX, event.clientY];
 +		var x = clickedPos[0]-imagePos[0]+1;
 +		var y = clickedPos[1]-imagePos[1]+1;
 +		var id = options['EventTarget'];
 +		var x_input = INPUT({type:'hidden',name:id+'_x',value:x});
 +		var y_input = INPUT({type:'hidden',name:id+'_y',value:y});
 +		this.element.parentNode.appendChild(x_input);
 +		this.element.parentNode.appendChild(y_input);		
 +	}
 +});
 +
 +
 +/**
 + * Radio button, only initialize if not already checked.
 + */
 +Prado.WebUI.TRadioButton = Class.extend(Prado.WebUI.PostBackControl);
 +Prado.WebUI.TRadioButton.prototype.onRadioButtonInitialize = Prado.WebUI.TRadioButton.prototype.initialize;
 +Object.extend(Prado.WebUI.TRadioButton.prototype,
 +{
 +	initialize : function(options)
 +	{
 +		this.element = $(options['ID']);
 +		if(!this.element.checked)
 +			this.onRadioButtonInitialize(options);
 +	}
 +});
 +
 +
 +Prado.WebUI.TTextBox = Class.extend(Prado.WebUI.PostBackControl,
 +{
 +	onInit : function(options)
 +	{
 +		if(options['TextMode'] != 'MultiLine')
 +			Event.observe(this.element, "keydown", this.handleReturnKey.bind(this));
 +		Event.observe(this.element, "change", Prado.PostBack.bindEvent(this,options));
 +	},
 +
 +	handleReturnKey : function(e)
 +	{
 +		 if(Event.keyCode(e) == Event.KEY_RETURN)
 +        {
 +			var target = Event.element(e);
 +			if(target)
 +			{
 +				Event.fireEvent(target, "change");
 +				Event.stop(e);
 +			}
 +		}
 +	}
 +});
 +
 +Prado.WebUI.TListControl = Class.extend(Prado.WebUI.PostBackControl,
 +{
 +	onInit : function(options)
 +	{
 +		Event.observe(this.element, "change", Prado.PostBack.bindEvent(this,options));
 +	}
 +});
 +
 +Prado.WebUI.TListBox = Class.extend(Prado.WebUI.TListControl);
 +Prado.WebUI.TDropDownList = Class.extend(Prado.WebUI.TListControl);
 +
 +Prado.WebUI.DefaultButton = Class.create();
 +Prado.WebUI.DefaultButton.prototype = 
 +{
 +	initialize : function(options)
 +	{
 +		this.options = options;
 +		this._event = this.triggerEvent.bindEvent(this);
 +		Event.observe(options['Panel'], 'keydown', this._event);
 +	},
 +
 +	triggerEvent : function(ev, target)
 +	{
 +		var enterPressed = Event.keyCode(ev) == Event.KEY_RETURN;
 +		var isTextArea = Event.element(ev).tagName.toLowerCase() == "textarea";
 +		if(enterPressed && !isTextArea)
 +		{
 +			var defaultButton = $(this.options['Target']);
 +			if(defaultButton)
 +			{
 +				this.triggered = true;
 +				Event.fireEvent(defaultButton, this.options['Event']);
 +				Event.stop(ev);
 +			}
 +		}
 +	}
 +};
 +
 +Prado.WebUI.TTextHighlighter=Class.create();
 +Prado.WebUI.TTextHighlighter.prototype=
 +{
 +	initialize:function(id)
 +	{
 +		if(!window.clipboardData) return;
 +		var options =
 +		{
 +			href : 'javascript:;/'+'/copy code to clipboard',
 +			onclick : 'Prado.WebUI.TTextHighlighter.copy(this)',
 +			onmouseover : 'Prado.WebUI.TTextHighlighter.hover(this)',
 +			onmouseout : 'Prado.WebUI.TTextHighlighter.out(this)'
 +		}
 +		var div = DIV({className:'copycode'}, A(options, 'Copy Code'));
 +		document.write(DIV(null,div).innerHTML);
 +	}
 +};
 +
 +Object.extend(Prado.WebUI.TTextHighlighter,
 +{
 +	copy : function(obj)
 +	{
 +		var parent = obj.parentNode.parentNode.parentNode;
 +		var text = '';
 +		for(var i = 0; i < parent.childNodes.length; i++)
 +		{
 +			var node = parent.childNodes[i];
 +			if(node.innerText)
 +				text += node.innerText == 'Copy Code' ? '' : node.innerText;
 +			else
 +				text += node.nodeValue;
 +		}
 +		if(text.length > 0)
 +			window.clipboardData.setData("Text", text);
 +	},
 +
 +	hover : function(obj)
 +	{
 +		obj.parentNode.className = "copycode copycode_hover";
 +	},
 +
 +	out : function(obj)
 +	{
 +		obj.parentNode.className = "copycode";
 +	}
 +});
 + + +Prado.WebUI.TRatingList = Class.create();	
 +Prado.WebUI.TRatingList.prototype = 
 +{
 +	selectedIndex : -1,
 +
 +	initialize : function(options)
 +	{
 +		this.options = options;
 +		this.element = $(options['ID']);
 +		Element.addClassName(this.element,options.cssClass);
 +		this.radios = document.getElementsByName(options.field);
 +		for(var i = 0; i<this.radios.length; i++)
 +		{
 +			Event.observe(this.radios[i].parentNode, "mouseover", this.hover.bindEvent(this,i));
 +			Event.observe(this.radios[i].parentNode, "mouseout", this.recover.bindEvent(this,i));
 +			Event.observe(this.radios[i].parentNode, "click", this.click.bindEvent(this, i));
 +		}		
 +		this.caption = CAPTION();
 +		this.element.appendChild(this.caption);
 +		this.selectedIndex = options.selectedIndex;
 +		this.setRating(this.selectedIndex);
 +	},
 +	
 +	hover : function(ev,index)
 +	{
 +		for(var i = 0; i<this.radios.length; i++)
 +			this.radios[i].parentNode.className = (i<=index) ? "rating_hover" : "";
 +		this.setCaption(index);
 +	},
 +	
 +	recover : function(ev,index)
 +	{
 +		for(var i = 0; i<=index; i++)
 +			Element.removeClassName(this.radios[i].parentNode, "rating_hover");
 +		this.setRating(this.selectedIndex);
 +	},
 +	
 +	click : function(ev, index)
 +	{
 +		for(var i = 0; i<this.radios.length; i++)
 +			this.radios[i].checked = (i == index);
 +		this.selectedIndex = index;
 +		this.setRating(index);
 +		if(isFunction(this.options.onChange))
 +			this.options.onChange(this,index);		
 +	},
 +	
 +	setRating: function(index)
 +	{
 +		for(var i = 0; i<=index; i++)
 +			this.radios[i].parentNode.className = "rating_selected";
 +		this.setCaption(index);
 +	},
 +	
 +	setCaption : function(index)
 +	{
 +		this.caption.innerHTML = index > -1 ? 
 +			this.radios[index].value : this.options.caption;	
 +	}
 +} + diff --git a/framework/Web/Javascripts/js/debug/rico.js b/framework/Web/Javascripts/js/debug/rico.js new file mode 100644 index 00000000..07ae9537 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/rico.js @@ -0,0 +1,3503 @@ +/**
 +  *
 +  *  Copyright 2005 Sabre Airline Solutions
 +  *
 +  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
 +  *  file except in compliance with the License. You may obtain a copy of the License at
 +  *
 +  *         http://www.apache.org/licenses/LICENSE-2.0
 +  *
 +  *  Unless required by applicable law or agreed to in writing, software distributed under the
 +  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 +  *  either express or implied. See the License for the specific language governing permissions
 +  *  and limitations under the License.
 +  **/
 +
 +
 +//-------------------- rico.js
 +var Rico = {
 +  Version: '1.1rc1',
 +  prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
 +}
 +
 +/*
 +if((typeof Prototype=='undefined') || Rico.prototypeVersion < 1.3)
 +      throw("Rico requires the Prototype JavaScript framework >= 1.3");
 +*/
 +Rico.ArrayExtensions = new Array();
 +
 +if (Object.prototype.extend) {
 +   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
 +}else{
 +  Object.prototype.extend = function(object) {
 +    return Object.extend.apply(this, [this, object]);
 +  }
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
 +}
 +
 +if (Array.prototype.push) {
 +   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
 +}
 +
 +if (!Array.prototype.remove) {
 +   Array.prototype.remove = function(dx) {
 +      if( isNaN(dx) || dx > this.length )
 +         return false;
 +      for( var i=0,n=0; i<this.length; i++ )
 +         if( i != dx )
 +            this[n++]=this[i];
 +      this.length-=1;
 +   };
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
 +}
 +
 +if (!Array.prototype.removeItem) {
 +   Array.prototype.removeItem = function(item) {
 +      for ( var i = 0 ; i < this.length ; i++ )
 +         if ( this[i] == item ) {
 +            this.remove(i);
 +            break;
 +         }
 +   };
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
 +}
 +
 +if (!Array.prototype.indices) {
 +   Array.prototype.indices = function() {
 +      var indexArray = new Array();
 +      for ( index in this ) {
 +         var ignoreThis = false;
 +         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
 +            if ( this[index] == Rico.ArrayExtensions[i] ) {
 +               ignoreThis = true;
 +               break;
 +            }
 +         }
 +         if ( !ignoreThis )
 +            indexArray[ indexArray.length ] = index;
 +      }
 +      return indexArray;
 +   }
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
 +}
 +
 +// Create the loadXML method and xml getter for Mozilla
 +if ( window.DOMParser &&
 +	  window.XMLSerializer &&
 +	  window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
 +
 +   if (!Document.prototype.loadXML) {
 +      Document.prototype.loadXML = function (s) {
 +         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
 +         while (this.hasChildNodes())
 +            this.removeChild(this.lastChild);
 +
 +         for (var i = 0; i < doc2.childNodes.length; i++) {
 +            this.appendChild(this.importNode(doc2.childNodes[i], true));
 +         }
 +      };
 +	}
 +
 +	Document.prototype.__defineGetter__( "xml",
 +	   function () {
 +		   return (new XMLSerializer()).serializeToString(this);
 +	   }
 +	 );
 +}
 +
 +document.getElementsByTagAndClassName = function(tagName, className) {
 +  if ( tagName == null )
 +     tagName = '*';
 +
 +  var children = document.getElementsByTagName(tagName) || document.all;
 +  var elements = new Array();
 +
 +  if ( className == null )
 +    return children;
 +
 +  for (var i = 0; i < children.length; i++) {
 +    var child = children[i];
 +    var classNames = child.className.split(' ');
 +    for (var j = 0; j < classNames.length; j++) {
 +      if (classNames[j] == className) {
 +        elements.push(child);
 +        break;
 +      }
 +    }
 +  }
 +
 +  return elements;
 +}
 +
 +
 +//-------------------- ricoAccordion.js
 +Rico.Accordion = Class.create();
 +
 +Rico.Accordion.prototype = {
 +
 +   initialize: function(container, options) {
 +      this.container            = $(container);
 +      this.lastExpandedTab      = null;
 +      this.accordionTabs        = new Array();
 +      this.setOptions(options);
 +      this._attachBehaviors();
 +      if(!container) return;
 +
 +      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
 +      // validate onloadShowTab
 +       if (this.options.onLoadShowTab >= this.accordionTabs.length)
 +        this.options.onLoadShowTab = 0;
 +
 +      // set the initial visual state...
 +      for ( var i=0 ; i < this.accordionTabs.length ; i++ )
 +      {
 +        if (i != this.options.onLoadShowTab){
 +         this.accordionTabs[i].collapse();
 +         this.accordionTabs[i].content.style.display = 'none';
 +        }
 +      }
 +      this.lastExpandedTab = this.accordionTabs[this.options.onLoadShowTab];
 +      if (this.options.panelHeight == 'auto'){
 +          var tabToCheck = (this.options.onloadShowTab === 0)? 1 : 0;
 +          var titleBarSize = parseInt(RicoUtil.getElementsComputedStyle(this.accordionTabs[tabToCheck].titleBar, 'height'));
 +          if (isNaN(titleBarSize))
 +            titleBarSize = this.accordionTabs[tabToCheck].titleBar.offsetHeight;
 +
 +          var totalTitleBarSize = this.accordionTabs.length * titleBarSize;
 +          var parentHeight = parseInt(RicoUtil.getElementsComputedStyle(this.container.parentNode, 'height'));
 +          if (isNaN(parentHeight))
 +            parentHeight = this.container.parentNode.offsetHeight;
 +
 +          this.options.panelHeight = parentHeight - totalTitleBarSize-2;
 +      }
 +
 +      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
 +      this.lastExpandedTab.showExpanded();
 +      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
 +
 +   },
 +
 +   setOptions: function(options) {
 +      this.options = {
 +         expandedBg          : '#63699c',
 +         hoverBg             : '#63699c',
 +         collapsedBg         : '#6b79a5',
 +         expandedTextColor   : '#ffffff',
 +         expandedFontWeight  : 'bold',
 +         hoverTextColor      : '#ffffff',
 +         collapsedTextColor  : '#ced7ef',
 +         collapsedFontWeight : 'normal',
 +         hoverTextColor      : '#ffffff',
 +         borderColor         : '#1f669b',
 +         panelHeight         : 200,
 +         onHideTab           : null,
 +         onShowTab           : null,
 +         onLoadShowTab       : 0
 +      }
 +      Object.extend(this.options, options || {});
 +   },
 +
 +   showTabByIndex: function( anIndex, animate ) {
 +      var doAnimate = arguments.length == 1 ? true : animate;
 +      this.showTab( this.accordionTabs[anIndex], doAnimate );
 +   },
 +
 +   showTab: function( accordionTab, animate ) {
 +
 +      var doAnimate = arguments.length == 1 ? true : animate;
 +
 +      if ( this.options.onHideTab )
 +         this.options.onHideTab(this.lastExpandedTab);
 +
 +      this.lastExpandedTab.showCollapsed();
 +      var accordion = this;
 +      var lastExpandedTab = this.lastExpandedTab;
 +
 +      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
 +      accordionTab.content.style.display = '';
 +
 +      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
 +
 +      if ( doAnimate ) {
 +         new Effect.AccordionSize( this.lastExpandedTab.content,
 +                                   accordionTab.content,
 +                                   1,
 +                                   this.options.panelHeight,
 +                                   100, 10,
 +                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
 +         this.lastExpandedTab = accordionTab;
 +      }
 +      else {
 +         this.lastExpandedTab.content.style.height = "1px";
 +         accordionTab.content.style.height = this.options.panelHeight + "px";
 +         this.lastExpandedTab = accordionTab;
 +         this.showTabDone(lastExpandedTab);
 +      }
 +   },
 +
 +   showTabDone: function(collapsedTab) {
 +      collapsedTab.content.style.display = 'none';
 +      this.lastExpandedTab.showExpanded();
 +      if ( this.options.onShowTab )
 +         this.options.onShowTab(this.lastExpandedTab);
 +   },
 +
 +   _attachBehaviors: function() {
 +      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
 +      for ( var i = 0 ; i < panels.length ; i++ ) {
 +
 +         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
 +         if ( tabChildren.length != 2 )
 +            continue; // unexpected
 +
 +         var tabTitleBar   = tabChildren[0];
 +         var tabContentBox = tabChildren[1];
 +         this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
 +      }
 +   },
 +
 +   _getDirectChildrenByTag: function(e, tagName) {
 +      var kids = new Array();
 +      var allKids = e.childNodes;
 +      for( var i = 0 ; i < allKids.length ; i++ )
 +         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
 +            kids.push(allKids[i]);
 +      return kids;
 +   }
 +
 +};
 +
 +Rico.Accordion.Tab = Class.create();
 +
 +Rico.Accordion.Tab.prototype = {
 +
 +   initialize: function(accordion, titleBar, content) {
 +      this.accordion = accordion;
 +      this.titleBar  = titleBar;
 +      this.content   = content;
 +      this._attachBehaviors();
 +   },
 +
 +   collapse: function() {
 +      this.showCollapsed();
 +      this.content.style.height = "1px";
 +   },
 +
 +   showCollapsed: function() {
 +      this.expanded = false;
 +      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
 +      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
 +      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
 +      this.content.style.overflow = "hidden";
 +   },
 +
 +   showExpanded: function() {
 +      this.expanded = true;
 +      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
 +      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
 +      this.content.style.overflow         = "visible";
 +   },
 +
 +   titleBarClicked: function(e) {
 +      if ( this.accordion.lastExpandedTab == this )
 +         return;
 +      this.accordion.showTab(this);
 +   },
 +
 +   hover: function(e) {
 +		this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
 +		this.titleBar.style.color           = this.accordion.options.hoverTextColor;
 +   },
 +
 +   unhover: function(e) {
 +      if ( this.expanded ) {
 +         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
 +         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
 +      }
 +      else {
 +         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
 +         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
 +      }
 +   },
 +
 +   _attachBehaviors: function() {
 +      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
 +      this.content.style.borderTopWidth    = "0px";
 +      this.content.style.borderBottomWidth = "0px";
 +      this.content.style.margin            = "0px";
 +
 +      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
 +      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
 +      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
 +   }
 +
 +};
 +
 +
 +//-------------------- ricoAjaxEngine.js
 +/*
 +Rico.AjaxEngine = Class.create();
 +
 +Rico.AjaxEngine.prototype = {
 +
 +   initialize: function() {
 +      this.ajaxElements = new Array();
 +      this.ajaxObjects  = new Array();
 +      this.requestURLS  = new Array();
 +      this.options = {};
 +   },
 +
 +   registerAjaxElement: function( anId, anElement ) {
 +      if ( !anElement )
 +         anElement = $(anId);
 +      this.ajaxElements[anId] = anElement;
 +   },
 +
 +   registerAjaxObject: function( anId, anObject ) {
 +      this.ajaxObjects[anId] = anObject;
 +   },
 +
 +   registerRequest: function (requestLogicalName, requestURL) {
 +      this.requestURLS[requestLogicalName] = requestURL;
 +   },
 +
 +   sendRequest: function(requestName, options) {
 +      // Allow for backwards Compatibility
 +      if ( arguments.length >= 2 )
 +       if (typeof arguments[1] == 'string')
 +         options = {parameters: this._createQueryString(arguments, 1)};
 +      this.sendRequestWithData(requestName, null, options);
 +   },
 +
 +   sendRequestWithData: function(requestName, xmlDocument, options) {
 +      var requestURL = this.requestURLS[requestName];
 +      if ( requestURL == null )
 +         return;
 +
 +      // Allow for backwards Compatibility
 +      if ( arguments.length >= 3 )
 +        if (typeof arguments[2] == 'string')
 +          options.parameters = this._createQueryString(arguments, 2);
 +
 +      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
 +   },
 +
 +   sendRequestAndUpdate: function(requestName,container,options) {
 +      // Allow for backwards Compatibility
 +      if ( arguments.length >= 3 )
 +        if (typeof arguments[2] == 'string')
 +          options.parameters = this._createQueryString(arguments, 2);
 +
 +      this.sendRequestWithDataAndUpdate(requestName, null, container, options);
 +   },
 +
 +   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
 +      var requestURL = this.requestURLS[requestName];
 +      if ( requestURL == null )
 +         return;
 +
 +      // Allow for backwards Compatibility
 +      if ( arguments.length >= 4 )
 +        if (typeof arguments[3] == 'string')
 +          options.parameters = this._createQueryString(arguments, 3);
 +
 +      var updaterOptions = this._requestOptions(options,xmlDocument);
 +      // Turn off onComplete
 +      //updaterOptions.onComplete = null;
 +
 +      new Ajax.Updater(container, requestURL, updaterOptions);
 +   },
 +
 +   // Private -- not part of intended engine API --------------------------------------------------------------------
 +
 +   _requestOptions: function(options,xmlDoc) {
 +      var requestHeaders = ['X-Rico-Version', Rico.Version ];
 +      var sendMethod = 'post';
 +      if ( xmlDoc == null )
 +        if (Rico.prototypeVersion < 1.4)
 +        requestHeaders.push( 'Content-type', 'text/xml' );
 +      else
 +          sendMethod = 'get';
 +      (!options) ? options = {} : '';
 +
 +      // Check and keep any user onComplete functions
 +      if (options.onComplete)
 +           options.onRicoComplete = options.onComplete;
 +      // Fix onComplete
 +      if (options.overrideOnComplete)
 +        options.onComplete = options.overrideOnComplete;
 +      else
 +        options.onComplete = this._onRequestComplete.bind(this);
 +
 +     // Set the default options and extend with any user options
 +     this.options = {
 +                     requestHeaders: requestHeaders,
 +                     parameters:     options.parameters,
 +                     postBody:       xmlDoc,
 +                     method:         sendMethod,
 +                     onComplete:     options.onComplete
 +                    };
 +     // Set any user options:
 +     Object.extend(this.options, options);
 +     return this.options;
 +   },
 +
 +   _createQueryString: function( theArgs, offset ) {
 +      var queryString = ""
 +      for ( var i = offset ; i < theArgs.length ; i++ ) {
 +          if ( i != offset )
 +            queryString += "&";
 +
 +          var anArg = theArgs[i];
 +
 +          if ( anArg.name != undefined && anArg.value != undefined ) {
 +            queryString += anArg.name +  "=" + escape(anArg.value);
 +          }
 +          else {
 +             var ePos  = anArg.indexOf('=');
 +             var argName  = anArg.substring( 0, ePos );
 +             var argValue = anArg.substring( ePos + 1 );
 +             queryString += argName + "=" + escape(argValue);
 +          }
 +      }
 +      return queryString;
 +   },
 +
 +   _onRequestComplete : function(request) {
 +      if(!request)
 +          return;
 +      // User can set an onFailure option - which will be called by prototype
 +      if (request.status != 200)
 +        return;
 +
 +      var response = request.responseXML.getElementsByTagName("ajax-response");
 +      if (response == null || response.length != 1)
 +         return;
 +      this._processAjaxResponse( response[0].childNodes );
 +
 +      // Check if user has set a onComplete function
 +      var onRicoComplete = this.options.onRicoComplete;
 +      if (onRicoComplete != null)
 +          onRicoComplete();
 +   },
 +
 +   _processAjaxResponse: function( xmlResponseElements ) {
 +      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
 +         var responseElement = xmlResponseElements[i];
 +
 +         // only process nodes of type element.....
 +         if ( responseElement.nodeType != 1 )
 +            continue;
 +
 +         var responseType = responseElement.getAttribute("type");
 +         var responseId   = responseElement.getAttribute("id");
 +
 +         if ( responseType == "object" )
 +            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
 +         else if ( responseType == "element" )
 +            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
 +         else
 +            alert('unrecognized AjaxResponse type : ' + responseType );
 +      }
 +   },
 +
 +   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
 +      ajaxObject.ajaxUpdate( responseElement );
 +   },
 +
 +   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
 +      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
 +   }
 +
 +}
 +
 +var ajaxEngine = new Rico.AjaxEngine();
 +*/
 +
 +//-------------------- ricoColor.js
 +/*Rico.Color = Class.create();
 +
 +Rico.Color.prototype = {
 +
 +   initialize: function(red, green, blue) {
 +      this.rgb = { r: red, g : green, b : blue };
 +   },
 +
 +   setRed: function(r) {
 +      this.rgb.r = r;
 +   },
 +
 +   setGreen: function(g) {
 +      this.rgb.g = g;
 +   },
 +
 +   setBlue: function(b) {
 +      this.rgb.b = b;
 +   },
 +
 +   setHue: function(h) {
 +
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.h = h;
 +
 +      // convert back to RGB...
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
 +   },
 +
 +   setSaturation: function(s) {
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.s = s;
 +
 +      // convert back to RGB and set values...
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
 +   },
 +
 +   setBrightness: function(b) {
 +      // get an HSB model, and set the new hue...
 +      var hsb = this.asHSB();
 +      hsb.b = b;
 +
 +      // convert back to RGB and set values...
 +      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
 +   },
 +
 +   darken: function(percent) {
 +      var hsb  = this.asHSB();
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
 +   },
 +
 +   brighten: function(percent) {
 +      var hsb  = this.asHSB();
 +      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
 +   },
 +
 +   blend: function(other) {
 +      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
 +      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
 +      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
 +   },
 +
 +   isBright: function() {
 +      var hsb = this.asHSB();
 +      return this.asHSB().b > 0.5;
 +   },
 +
 +   isDark: function() {
 +      return ! this.isBright();
 +   },
 +
 +   asRGB: function() {
 +      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
 +   },
 +
 +   asHex: function() {
 +      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
 +   },
 +
 +   asHSB: function() {
 +      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
 +   },
 +
 +   toString: function() {
 +      return this.asHex();
 +   }
 +
 +};
 +
 +Rico.Color.createFromHex = function(hexCode) {
 +
 +   if ( hexCode.indexOf('#') == 0 )
 +      hexCode = hexCode.substring(1);
 +   var red   = hexCode.substring(0,2);
 +   var green = hexCode.substring(2,4);
 +   var blue  = hexCode.substring(4,6);
 +   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
 +}
 +
 +/**
 + * Factory method for creating a color from the background of
 + * an HTML element.
 + *
 +Rico.Color.createColorFromBackground = function(elem) {
 +
 +   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
 +
 +   if ( actualColor == "transparent" && elem.parent )
 +      return Rico.Color.createColorFromBackground(elem.parent);
 +
 +   if ( actualColor == null )
 +      return new Rico.Color(255,255,255);
 +
 +   if ( actualColor.indexOf("rgb(") == 0 ) {
 +      var colors = actualColor.substring(4, actualColor.length - 1 );
 +      var colorArray = colors.split(",");
 +      return new Rico.Color( parseInt( colorArray[0] ),
 +                            parseInt( colorArray[1] ),
 +                            parseInt( colorArray[2] )  );
 +
 +   }
 +   else if ( actualColor.indexOf("#") == 0 ) {
 +      var redPart   = parseInt(actualColor.substring(1,3), 16);
 +      var greenPart = parseInt(actualColor.substring(3,5), 16);
 +      var bluePart  = parseInt(actualColor.substring(5), 16);
 +      return new Rico.Color( redPart, greenPart, bluePart );
 +   }
 +   else
 +      return new Rico.Color(255,255,255);
 +}
 +
 +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
 +
 +   var red   = 0;
 +	var green = 0;
 +	var blue  = 0;
 +
 +   if (saturation == 0) {
 +      red = parseInt(brightness * 255.0 + 0.5);
 +	   green = red;
 +	   blue = red;
 +	}
 +	else {
 +      var h = (hue - Math.floor(hue)) * 6.0;
 +      var f = h - Math.floor(h);
 +      var p = brightness * (1.0 - saturation);
 +      var q = brightness * (1.0 - saturation * f);
 +      var t = brightness * (1.0 - (saturation * (1.0 - f)));
 +
 +      switch (parseInt(h)) {
 +         case 0:
 +            red   = (brightness * 255.0 + 0.5);
 +            green = (t * 255.0 + 0.5);
 +            blue  = (p * 255.0 + 0.5);
 +            break;
 +         case 1:
 +            red   = (q * 255.0 + 0.5);
 +            green = (brightness * 255.0 + 0.5);
 +            blue  = (p * 255.0 + 0.5);
 +            break;
 +         case 2:
 +            red   = (p * 255.0 + 0.5);
 +            green = (brightness * 255.0 + 0.5);
 +            blue  = (t * 255.0 + 0.5);
 +            break;
 +         case 3:
 +            red   = (p * 255.0 + 0.5);
 +            green = (q * 255.0 + 0.5);
 +            blue  = (brightness * 255.0 + 0.5);
 +            break;
 +         case 4:
 +            red   = (t * 255.0 + 0.5);
 +            green = (p * 255.0 + 0.5);
 +            blue  = (brightness * 255.0 + 0.5);
 +            break;
 +          case 5:
 +            red   = (brightness * 255.0 + 0.5);
 +            green = (p * 255.0 + 0.5);
 +            blue  = (q * 255.0 + 0.5);
 +            break;
 +	    }
 +	}
 +
 +   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
 +}
 +
 +Rico.Color.RGBtoHSB = function(r, g, b) {
 +
 +   var hue;
 +   var saturaton;
 +   var brightness;
 +
 +   var cmax = (r > g) ? r : g;
 +   if (b > cmax)
 +      cmax = b;
 +
 +   var cmin = (r < g) ? r : g;
 +   if (b < cmin)
 +      cmin = b;
 +
 +   brightness = cmax / 255.0;
 +   if (cmax != 0)
 +      saturation = (cmax - cmin)/cmax;
 +   else
 +      saturation = 0;
 +
 +   if (saturation == 0)
 +      hue = 0;
 +   else {
 +      var redc   = (cmax - r)/(cmax - cmin);
 +    	var greenc = (cmax - g)/(cmax - cmin);
 +    	var bluec  = (cmax - b)/(cmax - cmin);
 +
 +    	if (r == cmax)
 +    	   hue = bluec - greenc;
 +    	else if (g == cmax)
 +    	   hue = 2.0 + redc - bluec;
 +      else
 +    	   hue = 4.0 + greenc - redc;
 +
 +    	hue = hue / 6.0;
 +    	if (hue < 0)
 +    	   hue = hue + 1.0;
 +   }
 +
 +   return { h : hue, s : saturation, b : brightness };
 +}
 +*/
 +
 +//-------------------- ricoCorner.js
 +Rico.Corner = {
 +
 +   round: function(e, options) {
 +      var e = $(e);
 +      this._setOptions(options);
 +
 +      var color = this.options.color;
 +      if ( this.options.color == "fromElement" )
 +         color = this._background(e);
 +
 +      var bgColor = this.options.bgColor;
 +      if ( this.options.bgColor == "fromParent" )
 +         bgColor = this._background(e.offsetParent);
 +
 +      this._roundCornersImpl(e, color, bgColor);
 +   },
 +
 +   _roundCornersImpl: function(e, color, bgColor) {
 +      if(this.options.border)
 +         this._renderBorder(e,bgColor);
 +      if(this._isTopRounded())
 +         this._roundTopCorners(e,color,bgColor);
 +      if(this._isBottomRounded())
 +         this._roundBottomCorners(e,color,bgColor);
 +   },
 +
 +   _renderBorder: function(el,bgColor) {
 +      var borderValue = "1px solid " + this._borderColor(bgColor);
 +      var borderL = "border-left: "  + borderValue;
 +      var borderR = "border-right: " + borderValue;
 +      var style   = "style='" + borderL + ";" + borderR +  "'";
 +      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
 +   },
 +
 +   _roundTopCorners: function(el, color, bgColor) {
 +      var corner = this._createCorner(bgColor);
 +      for(var i=0 ; i < this.options.numSlices ; i++ )
 +         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
 +      el.style.paddingTop = 0;
 +      el.insertBefore(corner,el.firstChild);
 +   },
 +
 +   _roundBottomCorners: function(el, color, bgColor) {
 +      var corner = this._createCorner(bgColor);
 +      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
 +         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
 +      el.style.paddingBottom = 0;
 +      el.appendChild(corner);
 +   },
 +
 +   _createCorner: function(bgColor) {
 +      var corner = document.createElement("div");
 +      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
 +      return corner;
 +   },
 +
 +   _createCornerSlice: function(color,bgColor, n, position) {
 +      var slice = document.createElement("span");
 +
 +      var inStyle = slice.style;
 +      inStyle.backgroundColor = color;
 +      inStyle.display  = "block";
 +      inStyle.height   = "1px";
 +      inStyle.overflow = "hidden";
 +      inStyle.fontSize = "1px";
 +
 +      var borderColor = this._borderColor(color,bgColor);
 +      if ( this.options.border && n == 0 ) {
 +         inStyle.borderTopStyle    = "solid";
 +         inStyle.borderTopWidth    = "1px";
 +         inStyle.borderLeftWidth   = "0px";
 +         inStyle.borderRightWidth  = "0px";
 +         inStyle.borderBottomWidth = "0px";
 +         inStyle.height            = "0px"; // assumes css compliant box model
 +         inStyle.borderColor       = borderColor;
 +      }
 +      else if(borderColor) {
 +         inStyle.borderColor = borderColor;
 +         inStyle.borderStyle = "solid";
 +         inStyle.borderWidth = "0px 1px";
 +      }
 +
 +      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
 +         inStyle.height = "2px";
 +
 +      this._setMargin(slice, n, position);
 +      this._setBorder(slice, n, position);
 +      return slice;
 +   },
 +
 +   _setOptions: function(options) {
 +      this.options = {
 +         corners : "all",
 +         color   : "fromElement",
 +         bgColor : "fromParent",
 +         blend   : true,
 +         border  : false,
 +         compact : false
 +      }
 +      Object.extend(this.options, options || {});
 +
 +      this.options.numSlices = this.options.compact ? 2 : 4;
 +      if ( this._isTransparent() )
 +         this.options.blend = false;
 +   },
 +
 +   _whichSideTop: function() {
 +      if ( this._hasString(this.options.corners, "all", "top") )
 +         return "";
 +
 +      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
 +         return "";
 +
 +      if (this.options.corners.indexOf("tl") >= 0)
 +         return "left";
 +      else if (this.options.corners.indexOf("tr") >= 0)
 +          return "right";
 +      return "";
 +   },
 +
 +   _whichSideBottom: function() {
 +      if ( this._hasString(this.options.corners, "all", "bottom") )
 +         return "";
 +
 +      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
 +         return "";
 +
 +      if(this.options.corners.indexOf("bl") >=0)
 +         return "left";
 +      else if(this.options.corners.indexOf("br")>=0)
 +         return "right";
 +      return "";
 +   },
 +
 +   _borderColor : function(color,bgColor) {
 +      if ( color == "transparent" )
 +         return bgColor;
 +      else if ( this.options.border )
 +         return this.options.border;
 +      else if ( this.options.blend )
 +         return this._blend( bgColor, color );
 +      else
 +         return "";
 +   },
 +
 +
 +   _setMargin: function(el, n, corners) {
 +      var marginSize = this._marginSize(n);
 +      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
 +
 +      if ( whichSide == "left" ) {
 +         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
 +      }
 +      else if ( whichSide == "right" ) {
 +         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
 +      }
 +      else {
 +         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
 +      }
 +   },
 +
 +   _setBorder: function(el,n,corners) {
 +      var borderSize = this._borderSize(n);
 +      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
 +      if ( whichSide == "left" ) {
 +         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
 +      }
 +      else if ( whichSide == "right" ) {
 +         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
 +      }
 +      else {
 +         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
 +      }
 +      if (this.options.border != false)
 +        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
 +   },
 +
 +   _marginSize: function(n) {
 +      if ( this._isTransparent() )
 +         return 0;
 +
 +      var marginSizes          = [ 5, 3, 2, 1 ];
 +      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
 +      var compactMarginSizes   = [ 2, 1 ];
 +      var smBlendedMarginSizes = [ 1, 0 ];
 +
 +      if ( this.options.compact && this.options.blend )
 +         return smBlendedMarginSizes[n];
 +      else if ( this.options.compact )
 +         return compactMarginSizes[n];
 +      else if ( this.options.blend )
 +         return blendedMarginSizes[n];
 +      else
 +         return marginSizes[n];
 +   },
 +
 +   _borderSize: function(n) {
 +      var transparentBorderSizes = [ 5, 3, 2, 1 ];
 +      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
 +      var compactBorderSizes     = [ 1, 0 ];
 +      var actualBorderSizes      = [ 0, 2, 0, 0 ];
 +
 +      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
 +         return 1;
 +      else if ( this.options.compact )
 +         return compactBorderSizes[n];
 +      else if ( this.options.blend )
 +         return blendedBorderSizes[n];
 +      else if ( this.options.border )
 +         return actualBorderSizes[n];
 +      else if ( this._isTransparent() )
 +         return transparentBorderSizes[n];
 +      return 0;
 +   },
 +
 +   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
 +   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
 +   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
 +   _isTransparent: function() { return this.options.color == "transparent"; },
 +   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
 +   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
 +   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
 +}
 +
 +
 +//-------------------- ricoDragAndDrop.js
 +/*
 +Rico.DragAndDrop = Class.create();
 +
 +Rico.DragAndDrop.prototype = {
 +
 +   initialize: function() {
 +      this.dropZones                = new Array();
 +      this.draggables               = new Array();
 +      this.currentDragObjects       = new Array();
 +      this.dragElement              = null;
 +      this.lastSelectedDraggable    = null;
 +      this.currentDragObjectVisible = false;
 +      this.interestedInMotionEvents = false;
 +      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
 +      this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
 +      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
 +   },
 +
 +   registerDropZone: function(aDropZone) {
 +      this.dropZones[ this.dropZones.length ] = aDropZone;
 +   },
 +
 +   deregisterDropZone: function(aDropZone) {
 +      var newDropZones = new Array();
 +      var j = 0;
 +      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
 +         if ( this.dropZones[i] != aDropZone )
 +            newDropZones[j++] = this.dropZones[i];
 +      }
 +
 +      this.dropZones = newDropZones;
 +   },
 +
 +   clearDropZones: function() {
 +      this.dropZones = new Array();
 +   },
 +
 +   registerDraggable: function( aDraggable ) {
 +      this.draggables[ this.draggables.length ] = aDraggable;
 +      this._addMouseDownHandler( aDraggable );
 +   },
 +
 +   clearSelection: function() {
 +      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
 +         this.currentDragObjects[i].deselect();
 +      this.currentDragObjects = new Array();
 +      this.lastSelectedDraggable = null;
 +   },
 +
 +   hasSelection: function() {
 +      return this.currentDragObjects.length > 0;
 +   },
 +
 +   setStartDragFromElement: function( e, mouseDownElement ) {
 +      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
 +      this.startx = e.screenX - this.origPos.x
 +      this.starty = e.screenY - this.origPos.y
 +      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
 +      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
 +      //this.adjustedForDraggableSize = false;
 +
 +      this.interestedInMotionEvents = this.hasSelection();
 +      this._terminateEvent(e);
 +   },
 +
 +   updateSelection: function( draggable, extendSelection ) {
 +      if ( ! extendSelection )
 +         this.clearSelection();
 +
 +      if ( draggable.isSelected() ) {
 +         this.currentDragObjects.removeItem(draggable);
 +         draggable.deselect();
 +         if ( draggable == this.lastSelectedDraggable )
 +            this.lastSelectedDraggable = null;
 +      }
 +      else {
 +         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
 +         draggable.select();
 +         this.lastSelectedDraggable = draggable;
 +      }
 +   },
 +
 +   _mouseDownHandler: function(e) {
 +      if ( arguments.length == 0 )
 +         e = event;
 +
 +      // if not button 1 ignore it...
 +      var nsEvent = e.which != undefined;
 +      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
 +         return;
 +
 +      var eventTarget      = e.target ? e.target : e.srcElement;
 +      var draggableObject  = eventTarget.draggable;
 +
 +      var candidate = eventTarget;
 +      while (draggableObject == null && candidate.parentNode) {
 +         candidate = candidate.parentNode;
 +         draggableObject = candidate.draggable;
 +      }
 +
 +      if ( draggableObject == null )
 +         return;
 +
 +      this.updateSelection( draggableObject, e.ctrlKey );
 +
 +      // clear the drop zones postion cache...
 +      if ( this.hasSelection() )
 +         for ( var i = 0 ; i < this.dropZones.length ; i++ )
 +            this.dropZones[i].clearPositionCache();
 +
 +      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
 +   },
 +
 +
 +   _mouseMoveHandler: function(e) {
 +      var nsEvent = e.which != undefined;
 +      if ( !this.interestedInMotionEvents ) {
 +         //this._terminateEvent(e);
 +         return;
 +      }
 +
 +      if ( ! this.hasSelection() )
 +         return;
 +
 +      if ( ! this.currentDragObjectVisible )
 +         this._startDrag(e);
 +
 +      if ( !this.activatedDropZones )
 +         this._activateRegisteredDropZones();
 +
 +      //if ( !this.adjustedForDraggableSize )
 +      //   this._adjustForDraggableSize(e);
 +
 +      this._updateDraggableLocation(e);
 +      this._updateDropZonesHover(e);
 +
 +      this._terminateEvent(e);
 +   },
 +
 +   _makeDraggableObjectVisible: function(e)
 +   {
 +      if ( !this.hasSelection() )
 +         return;
 +
 +      var dragElement;
 +      if ( this.currentDragObjects.length > 1 )
 +         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
 +      else
 +         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
 +
 +      // go ahead and absolute position it...
 +      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
 +         dragElement.style.position = "absolute";
 +
 +      // need to parent him into the document...
 +      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
 +         document.body.appendChild(dragElement);
 +
 +      this.dragElement = dragElement;
 +      this._updateDraggableLocation(e);
 +
 +      this.currentDragObjectVisible = true;
 +   },
 +
 +   /**
 +   _adjustForDraggableSize: function(e) {
 +      var dragElementWidth  = this.dragElement.offsetWidth;
 +      var dragElementHeight = this.dragElement.offsetHeight;
 +      if ( this.startComponentX > dragElementWidth )
 +         this.startx -= this.startComponentX - dragElementWidth + 2;
 +      if ( e.offsetY ) {
 +         if ( this.startComponentY > dragElementHeight )
 +            this.starty -= this.startComponentY - dragElementHeight + 2;
 +      }
 +      this.adjustedForDraggableSize = true;
 +   },
 +   **/
 +/*
 +   _updateDraggableLocation: function(e) {
 +      var dragObjectStyle = this.dragElement.style;
 +      dragObjectStyle.left = (e.screenX - this.startx) + "px"
 +      dragObjectStyle.top  = (e.screenY - this.starty) + "px";
 +   },
 +
 +   _updateDropZonesHover: function(e) {
 +      var n = this.dropZones.length;
 +      for ( var i = 0 ; i < n ; i++ ) {
 +         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
 +            this.dropZones[i].hideHover();
 +      }
 +
 +      for ( var i = 0 ; i < n ; i++ ) {
 +         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
 +            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
 +               this.dropZones[i].showHover();
 +         }
 +      }
 +   },
 +
 +   _startDrag: function(e) {
 +      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
 +         this.currentDragObjects[i].startDrag();
 +
 +      this._makeDraggableObjectVisible(e);
 +   },
 +
 +   _mouseUpHandler: function(e) {
 +      if ( ! this.hasSelection() )
 +         return;
 +
 +      var nsEvent = e.which != undefined;
 +      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
 +         return;
 +
 +      this.interestedInMotionEvents = false;
 +
 +      if ( this.dragElement == null ) {
 +         this._terminateEvent(e);
 +         return;
 +      }
 +
 +      if ( this._placeDraggableInDropZone(e) )
 +         this._completeDropOperation(e);
 +      else {
 +         this._terminateEvent(e);
 +         new Effect.Position( this.dragElement,
 +                              this.origPos.x,
 +                              this.origPos.y,
 +                              200,
 +                              20,
 +                              { complete : this._doCancelDragProcessing.bind(this) } );
 +      }
 +
 +     Event.stopObserving(document.body, "mousemove", this._mouseMove);
 +     Event.stopObserving(document.body, "mouseup",  this._mouseUp);
 +   },
 +
 +   _retTrue: function () {
 +      return true;
 +   },
 +
 +   _completeDropOperation: function(e) {
 +      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
 +         if ( this.dragElement.parentNode != null )
 +            this.dragElement.parentNode.removeChild(this.dragElement);
 +      }
 +
 +      this._deactivateRegisteredDropZones();
 +      this._endDrag();
 +      this.clearSelection();
 +      this.dragElement = null;
 +      this.currentDragObjectVisible = false;
 +      this._terminateEvent(e);
 +   },
 +
 +   _doCancelDragProcessing: function() {
 +      this._cancelDrag();
 +
 +        if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
 +           if ( this.dragElement.parentNode != null )
 +              this.dragElement.parentNode.removeChild(this.dragElement);
 +
 +
 +      this._deactivateRegisteredDropZones();
 +      this.dragElement = null;
 +      this.currentDragObjectVisible = false;
 +   },
 +
 +   _placeDraggableInDropZone: function(e) {
 +      var foundDropZone = false;
 +      var n = this.dropZones.length;
 +      for ( var i = 0 ; i < n ; i++ ) {
 +         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
 +            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
 +               this.dropZones[i].hideHover();
 +               this.dropZones[i].accept(this.currentDragObjects);
 +               foundDropZone = true;
 +               break;
 +            }
 +         }
 +      }
 +
 +      return foundDropZone;
 +   },
 +
 +   _cancelDrag: function() {
 +      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
 +         this.currentDragObjects[i].cancelDrag();
 +   },
 +
 +   _endDrag: function() {
 +      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
 +         this.currentDragObjects[i].endDrag();
 +   },
 +
 +   _mousePointInDropZone: function( e, dropZone ) {
 +
 +      var absoluteRect = dropZone.getAbsoluteRect();
 +
 +      return e.clientX  > absoluteRect.left  &&
 +             e.clientX  < absoluteRect.right &&
 +             e.clientY  > absoluteRect.top   &&
 +             e.clientY  < absoluteRect.bottom;
 +   },
 +
 +   _addMouseDownHandler: function( aDraggable )
 +   {
 +       htmlElement  = aDraggable.getMouseDownHTMLElement();
 +      if ( htmlElement  != null ) {
 +         htmlElement.draggable = aDraggable;
 +         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
 +         Event.observe(htmlElement, "mousedown", this._mouseDown);
 +      }
 +   },
 +
 +   _activateRegisteredDropZones: function() {
 +      var n = this.dropZones.length;
 +      for ( var i = 0 ; i < n ; i++ ) {
 +         var dropZone = this.dropZones[i];
 +         if ( dropZone.canAccept(this.currentDragObjects) )
 +            dropZone.activate();
 +      }
 +
 +      this.activatedDropZones = true;
 +   },
 +
 +   _deactivateRegisteredDropZones: function() {
 +      var n = this.dropZones.length;
 +      for ( var i = 0 ; i < n ; i++ )
 +         this.dropZones[i].deactivate();
 +      this.activatedDropZones = false;
 +   },
 +
 +   _onmousedown: function () {
 +     Event.observe(document.body, "mousemove", this._mouseMove);
 +     Event.observe(document.body, "mouseup",  this._mouseUp);
 +   },
 +
 +   _terminateEvent: function(e) {
 +      if ( e.stopPropagation != undefined )
 +         e.stopPropagation();
 +      else if ( e.cancelBubble != undefined )
 +         e.cancelBubble = true;
 +
 +      if ( e.preventDefault != undefined )
 +         e.preventDefault();
 +      else
 +         e.returnValue = false;
 +   }
 +
 +}
 +
 +Rico.Draggable = Class.create();
 +
 +Rico.Draggable.prototype = {
 +
 +   initialize: function( type, htmlElement ) {
 +      this.type          = type;
 +      this.htmlElement   = $(htmlElement);
 +      this.selected      = false;
 +   },
 +
 +   /**
 +    *   Returns the HTML element that should have a mouse down event
 +    *   added to it in order to initiate a drag operation
 +    *
 +    **//*
 +   getMouseDownHTMLElement: function() {
 +      return this.htmlElement;
 +   },
 +
 +   select: function() {
 +      this.selected = true;
 +
 +      if ( this.showingSelected )
 +         return;
 +
 +      var htmlElement = this.getMouseDownHTMLElement();
 +
 +      var color = Rico.Color.createColorFromBackground(htmlElement);
 +      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
 +
 +      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
 +      htmlElement.style.backgroundColor = color.asHex();
 +      this.showingSelected = true;
 +   },
 +
 +   deselect: function() {
 +      this.selected = false;
 +      if ( !this.showingSelected )
 +         return;
 +
 +      var htmlElement = this.getMouseDownHTMLElement();
 +
 +      htmlElement.style.backgroundColor = this.saveBackground;
 +      this.showingSelected = false;
 +   },
 +
 +   isSelected: function() {
 +      return this.selected;
 +   },
 +
 +   startDrag: function() {
 +   },
 +
 +   cancelDrag: function() {
 +   },
 +
 +   endDrag: function() {
 +   },
 +
 +   getSingleObjectDragGUI: function() {
 +      return this.htmlElement;
 +   },
 +
 +   getMultiObjectDragGUI: function( draggables ) {
 +      return this.htmlElement;
 +   },
 +
 +   getDroppedGUI: function() {
 +      return this.htmlElement;
 +   },
 +
 +   toString: function() {
 +      return this.type + ":" + this.htmlElement + ":";
 +   }
 +
 +}
 +
 +Rico.Dropzone = Class.create();
 +
 +Rico.Dropzone.prototype = {
 +
 +   initialize: function( htmlElement ) {
 +      this.htmlElement  = $(htmlElement);
 +      this.absoluteRect = null;
 +   },
 +
 +   getHTMLElement: function() {
 +      return this.htmlElement;
 +   },
 +
 +   clearPositionCache: function() {
 +      this.absoluteRect = null;
 +   },
 +
 +   getAbsoluteRect: function() {
 +      if ( this.absoluteRect == null ) {
 +         var htmlElement = this.getHTMLElement();
 +         var pos = RicoUtil.toViewportPosition(htmlElement);
 +
 +         this.absoluteRect = {
 +            top:    pos.y,
 +            left:   pos.x,
 +            bottom: pos.y + htmlElement.offsetHeight,
 +            right:  pos.x + htmlElement.offsetWidth
 +         };
 +      }
 +      return this.absoluteRect;
 +   },
 +
 +   activate: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if (htmlElement == null  || this.showingActive)
 +         return;
 +
 +      this.showingActive = true;
 +      this.saveBackgroundColor = htmlElement.style.backgroundColor;
 +
 +      var fallbackColor = "#ffea84";
 +      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
 +      if ( currentColor == null )
 +         htmlElement.style.backgroundColor = fallbackColor;
 +      else {
 +         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
 +         htmlElement.style.backgroundColor = currentColor.asHex();
 +      }
 +   },
 +
 +   deactivate: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if (htmlElement == null || !this.showingActive)
 +         return;
 +
 +      htmlElement.style.backgroundColor = this.saveBackgroundColor;
 +      this.showingActive = false;
 +      this.saveBackgroundColor = null;
 +   },
 +
 +   showHover: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null || this.showingHover )
 +         return;
 +
 +      this.saveBorderWidth = htmlElement.style.borderWidth;
 +      this.saveBorderStyle = htmlElement.style.borderStyle;
 +      this.saveBorderColor = htmlElement.style.borderColor;
 +
 +      this.showingHover = true;
 +      htmlElement.style.borderWidth = "1px";
 +      htmlElement.style.borderStyle = "solid";
 +      //htmlElement.style.borderColor = "#ff9900";
 +      htmlElement.style.borderColor = "#ffff00";
 +   },
 +
 +   hideHover: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null || !this.showingHover )
 +         return;
 +
 +      htmlElement.style.borderWidth = this.saveBorderWidth;
 +      htmlElement.style.borderStyle = this.saveBorderStyle;
 +      htmlElement.style.borderColor = this.saveBorderColor;
 +      this.showingHover = false;
 +   },
 +
 +   canAccept: function(draggableObjects) {
 +      return true;
 +   },
 +
 +   accept: function(draggableObjects) {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null )
 +         return;
 +
 +      n = draggableObjects.length;
 +      for ( var i = 0 ; i < n ; i++ )
 +      {
 +         var theGUI = draggableObjects[i].getDroppedGUI();
 +         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
 +         {
 +            theGUI.style.position = "static";
 +            theGUI.style.top = "";
 +            theGUI.style.top = "";
 +         }
 +         htmlElement.appendChild(theGUI);
 +      }
 +   }
 +}
 +
 +var dndMgr = new Rico.DragAndDrop();
 +
 +
 +
 +//-------------------- ricoDraggable.js
 +Rico.Draggable = Class.create();
 +
 +Rico.Draggable.prototype = {
 +
 +   initialize: function( type, htmlElement ) {
 +      this.type          = type;
 +      this.htmlElement   = $(htmlElement);
 +      this.selected      = false;
 +   },
 +
 +   /**
 +    *   Returns the HTML element that should have a mouse down event
 +    *   added to it in order to initiate a drag operation
 +    *
 +    **//*
 +   getMouseDownHTMLElement: function() {
 +      return this.htmlElement;
 +   },
 +
 +   select: function() {
 +      this.selected = true;
 +
 +      if ( this.showingSelected )
 +         return;
 +
 +      var htmlElement = this.getMouseDownHTMLElement();
 +
 +      var color = Rico.Color.createColorFromBackground(htmlElement);
 +      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
 +
 +      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
 +      htmlElement.style.backgroundColor = color.asHex();
 +      this.showingSelected = true;
 +   },
 +
 +   deselect: function() {
 +      this.selected = false;
 +      if ( !this.showingSelected )
 +         return;
 +
 +      var htmlElement = this.getMouseDownHTMLElement();
 +
 +      htmlElement.style.backgroundColor = this.saveBackground;
 +      this.showingSelected = false;
 +   },
 +
 +   isSelected: function() {
 +      return this.selected;
 +   },
 +
 +   startDrag: function() {
 +   },
 +
 +   cancelDrag: function() {
 +   },
 +
 +   endDrag: function() {
 +   },
 +
 +   getSingleObjectDragGUI: function() {
 +      return this.htmlElement;
 +   },
 +
 +   getMultiObjectDragGUI: function( draggables ) {
 +      return this.htmlElement;
 +   },
 +
 +   getDroppedGUI: function() {
 +      return this.htmlElement;
 +   },
 +
 +   toString: function() {
 +      return this.type + ":" + this.htmlElement + ":";
 +   }
 +
 +}
 +
 +
 +//-------------------- ricoDropzone.js
 +Rico.Dropzone = Class.create();
 +
 +Rico.Dropzone.prototype = {
 +
 +   initialize: function( htmlElement ) {
 +      this.htmlElement  = $(htmlElement);
 +      this.absoluteRect = null;
 +   },
 +
 +   getHTMLElement: function() {
 +      return this.htmlElement;
 +   },
 +
 +   clearPositionCache: function() {
 +      this.absoluteRect = null;
 +   },
 +
 +   getAbsoluteRect: function() {
 +      if ( this.absoluteRect == null ) {
 +         var htmlElement = this.getHTMLElement();
 +         var pos = RicoUtil.toViewportPosition(htmlElement);
 +
 +         this.absoluteRect = {
 +            top:    pos.y,
 +            left:   pos.x,
 +            bottom: pos.y + htmlElement.offsetHeight,
 +            right:  pos.x + htmlElement.offsetWidth
 +         };
 +      }
 +      return this.absoluteRect;
 +   },
 +
 +   activate: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if (htmlElement == null  || this.showingActive)
 +         return;
 +
 +      this.showingActive = true;
 +      this.saveBackgroundColor = htmlElement.style.backgroundColor;
 +
 +      var fallbackColor = "#ffea84";
 +      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
 +      if ( currentColor == null )
 +         htmlElement.style.backgroundColor = fallbackColor;
 +      else {
 +         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
 +         htmlElement.style.backgroundColor = currentColor.asHex();
 +      }
 +   },
 +
 +   deactivate: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if (htmlElement == null || !this.showingActive)
 +         return;
 +
 +      htmlElement.style.backgroundColor = this.saveBackgroundColor;
 +      this.showingActive = false;
 +      this.saveBackgroundColor = null;
 +   },
 +
 +   showHover: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null || this.showingHover )
 +         return;
 +
 +      this.saveBorderWidth = htmlElement.style.borderWidth;
 +      this.saveBorderStyle = htmlElement.style.borderStyle;
 +      this.saveBorderColor = htmlElement.style.borderColor;
 +
 +      this.showingHover = true;
 +      htmlElement.style.borderWidth = "1px";
 +      htmlElement.style.borderStyle = "solid";
 +      //htmlElement.style.borderColor = "#ff9900";
 +      htmlElement.style.borderColor = "#ffff00";
 +   },
 +
 +   hideHover: function() {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null || !this.showingHover )
 +         return;
 +
 +      htmlElement.style.borderWidth = this.saveBorderWidth;
 +      htmlElement.style.borderStyle = this.saveBorderStyle;
 +      htmlElement.style.borderColor = this.saveBorderColor;
 +      this.showingHover = false;
 +   },
 +
 +   canAccept: function(draggableObjects) {
 +      return true;
 +   },
 +
 +   accept: function(draggableObjects) {
 +      var htmlElement = this.getHTMLElement();
 +      if ( htmlElement == null )
 +         return;
 +
 +      n = draggableObjects.length;
 +      for ( var i = 0 ; i < n ; i++ )
 +      {
 +         var theGUI = draggableObjects[i].getDroppedGUI();
 +         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
 +         {
 +            theGUI.style.position = "static";
 +            theGUI.style.top = "";
 +            theGUI.style.top = "";
 +         }
 +         htmlElement.appendChild(theGUI);
 +      }
 +   }
 +}
 +*/
 +
 +
 +//-------------------- ricoEffect.js
 +
 +/**
 +  *  Use the Effect namespace for effects.  If using scriptaculous effects
 +  *  this will already be defined, otherwise we'll just create an empty
 +  *  object for it...
 + **/
 +if ( window.Effect == undefined )
 +   Effect = {};
 +
 +Effect.SizeAndPosition = Class.create();
 +Effect.SizeAndPosition.prototype = {
 +
 +   initialize: function(element, x, y, w, h, duration, steps, options) {
 +      this.element = $(element);
 +      this.x = x;
 +      this.y = y;
 +      this.w = w;
 +      this.h = h;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[7] || {};
 +
 +      this.sizeAndPosition();
 +   },
 +
 +   sizeAndPosition: function() {
 +      if (this.isFinished()) {
 +         if(this.options.complete) this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +
 +      // Get original values: x,y = top left corner;  w,h = width height
 +      var currentX = this.element.offsetLeft;
 +      var currentY = this.element.offsetTop;
 +      var currentW = this.element.offsetWidth;
 +      var currentH = this.element.offsetHeight;
 +
 +      // If values not set, or zero, we do not modify them, and take original as final as well
 +      this.x = (this.x) ? this.x : currentX;
 +      this.y = (this.y) ? this.y : currentY;
 +      this.w = (this.w) ? this.w : currentW;
 +      this.h = (this.h) ? this.h : currentH;
 +
 +      // how much do we need to modify our values for each step?
 +      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
 +      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
 +      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
 +      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
 +
 +      this.moveBy(difX, difY);
 +      this.resizeBy(difW, difH);
 +
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   moveBy: function( difX, difY ) {
 +      var currentLeft = this.element.offsetLeft;
 +      var currentTop  = this.element.offsetTop;
 +      var intDifX     = parseInt(difX);
 +      var intDifY     = parseInt(difY);
 +
 +      var style = this.element.style;
 +      if ( intDifX != 0 )
 +         style.left = (currentLeft + intDifX) + "px";
 +      if ( intDifY != 0 )
 +         style.top  = (currentTop + intDifY) + "px";
 +   },
 +
 +   resizeBy: function( difW, difH ) {
 +      var currentWidth  = this.element.offsetWidth;
 +      var currentHeight = this.element.offsetHeight;
 +      var intDifW       = parseInt(difW);
 +      var intDifH       = parseInt(difH);
 +
 +      var style = this.element.style;
 +      if ( intDifW != 0 )
 +         style.width   = (currentWidth  + intDifW) + "px";
 +      if ( intDifH != 0 )
 +         style.height  = (currentHeight + intDifH) + "px";
 +   }
 +}
 +
 +Effect.Size = Class.create();
 +Effect.Size.prototype = {
 +
 +   initialize: function(element, w, h, duration, steps, options) {
 +      new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
 +  }
 +}
 +
 +Effect.Position = Class.create();
 +Effect.Position.prototype = {
 +
 +   initialize: function(element, x, y, duration, steps, options) {
 +      new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
 +  }
 +}
 +
 +Effect.Round = Class.create();
 +Effect.Round.prototype = {
 +
 +   initialize: function(tagName, className, options) {
 +      var elements = document.getElementsByTagAndClassName(tagName,className);
 +      for ( var i = 0 ; i < elements.length ; i++ )
 +         Rico.Corner.round( elements[i], options );
 +   }
 +};
 +
 +Effect.FadeTo = Class.create();
 +Effect.FadeTo.prototype = {
 +
 +   initialize: function( element, opacity, duration, steps, options) {
 +      this.element  = $(element);
 +      this.opacity  = opacity;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[4] || {};
 +      this.fadeTo();
 +   },
 +
 +   fadeTo: function() {
 +      if (this.isFinished()) {
 +         if(this.options.complete) this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +      var currentOpacity = this.getElementOpacity();
 +      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
 +
 +      this.changeOpacityBy(delta);
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
 +   },
 +
 +   changeOpacityBy: function(v) {
 +      var currentOpacity = this.getElementOpacity();
 +      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
 +      this.element.ricoOpacity = newOpacity;
 +
 +      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
 +      this.element.style.opacity = newOpacity; /*//*/;
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   getElementOpacity: function() {
 +      if ( this.element.ricoOpacity == undefined ) {
 +         var opacity = RicoUtil.getElementsComputedStyle(this.element, 'opacity');
 +         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
 +      }
 +      return parseFloat(this.element.ricoOpacity);
 +   }
 +}
 +
 +Effect.AccordionSize = Class.create();
 +
 +Effect.AccordionSize.prototype = {
 +
 +   initialize: function(e1, e2, start, end, duration, steps, options) {
 +      this.e1       = $(e1);
 +      this.e2       = $(e2);
 +      this.start    = start;
 +      this.end      = end;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[6] || {};
 +
 +      this.accordionSize();
 +   },
 +
 +   accordionSize: function() {
 +
 +      if (this.isFinished()) {
 +         // just in case there are round errors or such...
 +         this.e1.style.height = this.start + "px";
 +         this.e2.style.height = this.end + "px";
 +
 +         if(this.options.complete)
 +            this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +
 +      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
 +      this.resizeBy(diff);
 +
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   resizeBy: function(diff) {
 +      var h1Height = this.e1.offsetHeight;
 +      var h2Height = this.e2.offsetHeight;
 +      var intDiff = parseInt(diff);
 +      if ( diff != 0 ) {
 +         this.e1.style.height = (h1Height - intDiff) + "px";
 +         this.e2.style.height = (h2Height + intDiff) + "px";
 +      }
 +   }
 +
 +};
 +
 +
 +//-------------------- ricoEffects.js
 +
 +/**
 +  *  Use the Effect namespace for effects.  If using scriptaculous effects
 +  *  this will already be defined, otherwise we'll just create an empty
 +  *  object for it...
 + **/
 +if ( window.Effect == undefined )
 +   Effect = {};
 +
 +Effect.SizeAndPosition = Class.create();
 +Effect.SizeAndPosition.prototype = {
 +
 +   initialize: function(element, x, y, w, h, duration, steps, options) {
 +      this.element = $(element);
 +      this.x = x;
 +      this.y = y;
 +      this.w = w;
 +      this.h = h;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[7] || {};
 +
 +      this.sizeAndPosition();
 +   },
 +
 +   sizeAndPosition: function() {
 +      if (this.isFinished()) {
 +         if(this.options.complete) this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +
 +      // Get original values: x,y = top left corner;  w,h = width height
 +      var currentX = this.element.offsetLeft;
 +      var currentY = this.element.offsetTop;
 +      var currentW = this.element.offsetWidth;
 +      var currentH = this.element.offsetHeight;
 +
 +      // If values not set, or zero, we do not modify them, and take original as final as well
 +      this.x = (this.x) ? this.x : currentX;
 +      this.y = (this.y) ? this.y : currentY;
 +      this.w = (this.w) ? this.w : currentW;
 +      this.h = (this.h) ? this.h : currentH;
 +
 +      // how much do we need to modify our values for each step?
 +      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
 +      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
 +      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
 +      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
 +
 +      this.moveBy(difX, difY);
 +      this.resizeBy(difW, difH);
 +
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   moveBy: function( difX, difY ) {
 +      var currentLeft = this.element.offsetLeft;
 +      var currentTop  = this.element.offsetTop;
 +      var intDifX     = parseInt(difX);
 +      var intDifY     = parseInt(difY);
 +
 +      var style = this.element.style;
 +      if ( intDifX != 0 )
 +         style.left = (currentLeft + intDifX) + "px";
 +      if ( intDifY != 0 )
 +         style.top  = (currentTop + intDifY) + "px";
 +   },
 +
 +   resizeBy: function( difW, difH ) {
 +      var currentWidth  = this.element.offsetWidth;
 +      var currentHeight = this.element.offsetHeight;
 +      var intDifW       = parseInt(difW);
 +      var intDifH       = parseInt(difH);
 +
 +      var style = this.element.style;
 +      if ( intDifW != 0 )
 +         style.width   = (currentWidth  + intDifW) + "px";
 +      if ( intDifH != 0 )
 +         style.height  = (currentHeight + intDifH) + "px";
 +   }
 +}
 +
 +Effect.Size = Class.create();
 +Effect.Size.prototype = {
 +
 +   initialize: function(element, w, h, duration, steps, options) {
 +      new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
 +  }
 +}
 +
 +Effect.Position = Class.create();
 +Effect.Position.prototype = {
 +
 +   initialize: function(element, x, y, duration, steps, options) {
 +      new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
 +  }
 +}
 +
 +Effect.Round = Class.create();
 +Effect.Round.prototype = {
 +
 +   initialize: function(tagName, className, options) {
 +      var elements = document.getElementsByTagAndClassName(tagName,className);
 +      for ( var i = 0 ; i < elements.length ; i++ )
 +         Rico.Corner.round( elements[i], options );
 +   }
 +};
 +
 +Effect.FadeTo = Class.create();
 +Effect.FadeTo.prototype = {
 +
 +   initialize: function( element, opacity, duration, steps, options) {
 +      this.element  = $(element);
 +      this.opacity  = opacity;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[4] || {};
 +      this.fadeTo();
 +   },
 +
 +   fadeTo: function() {
 +      if (this.isFinished()) {
 +         if(this.options.complete) this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +      var currentOpacity = this.getElementOpacity();
 +      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
 +
 +      this.changeOpacityBy(delta);
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
 +   },
 +
 +   changeOpacityBy: function(v) {
 +      var currentOpacity = this.getElementOpacity();
 +      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
 +      this.element.ricoOpacity = newOpacity;
 +
 +      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
 +      this.element.style.opacity = newOpacity; /*//*/;
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   getElementOpacity: function() {
 +      if ( this.element.ricoOpacity == undefined ) {
 +         var opacity = RicoUtil.getElementsComputedStyle(this.element, 'opacity');
 +         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
 +      }
 +      return parseFloat(this.element.ricoOpacity);
 +   }
 +}
 +
 +Effect.AccordionSize = Class.create();
 +
 +Effect.AccordionSize.prototype = {
 +
 +   initialize: function(e1, e2, start, end, duration, steps, options) {
 +      this.e1       = $(e1);
 +      this.e2       = $(e2);
 +      this.start    = start;
 +      this.end      = end;
 +      this.duration = duration;
 +      this.steps    = steps;
 +      this.options  = arguments[6] || {};
 +
 +      this.accordionSize();
 +   },
 +
 +   accordionSize: function() {
 +
 +      if (this.isFinished()) {
 +         // just in case there are round errors or such...
 +         this.e1.style.height = this.start + "px";
 +         this.e2.style.height = this.end + "px";
 +
 +         if(this.options.complete)
 +            this.options.complete(this);
 +         return;
 +      }
 +
 +      if (this.timer)
 +         clearTimeout(this.timer);
 +
 +      var stepDuration = Math.round(this.duration/this.steps) ;
 +
 +      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
 +      this.resizeBy(diff);
 +
 +      this.duration -= stepDuration;
 +      this.steps--;
 +
 +      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
 +   },
 +
 +   isFinished: function() {
 +      return this.steps <= 0;
 +   },
 +
 +   resizeBy: function(diff) {
 +      var h1Height = this.e1.offsetHeight;
 +      var h2Height = this.e2.offsetHeight;
 +      var intDiff = parseInt(diff);
 +      if ( diff != 0 ) {
 +         this.e1.style.height = (h1Height - intDiff) + "px";
 +         this.e2.style.height = (h2Height + intDiff) + "px";
 +      }
 +   }
 +
 +};
 +
 +
 +//-------------------- ricoLiveGrid.js
 +// Rico.LiveGridMetaData -----------------------------------------------------
 +
 +Rico.LiveGridMetaData = Class.create();
 +
 +Rico.LiveGridMetaData.prototype = {
 +
 +   initialize: function( pageSize, totalRows, columnCount, options ) {
 +      this.pageSize  = pageSize;
 +      this.totalRows = totalRows;
 +      this.setOptions(options);
 +      this.ArrowHeight = 16;
 +      this.columnCount = columnCount;
 +   },
 +
 +   setOptions: function(options) {
 +      this.options = {
 +         largeBufferSize    : 7.0,   // 7 pages
 +         nearLimitFactor    : 0.2    // 20% of buffer
 +      };
 +      Object.extend(this.options, options || {});
 +   },
 +
 +   getPageSize: function() {
 +      return this.pageSize;
 +   },
 +
 +   getTotalRows: function() {
 +      return this.totalRows;
 +   },
 +
 +   setTotalRows: function(n) {
 +      this.totalRows = n;
 +   },
 +
 +   getLargeBufferSize: function() {
 +      return parseInt(this.options.largeBufferSize * this.pageSize);
 +   },
 +
 +   getLimitTolerance: function() {
 +      return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
 +   }
 +};
 +
 +// Rico.LiveGridScroller -----------------------------------------------------
 +
 +Rico.LiveGridScroller = Class.create();
 +
 +Rico.LiveGridScroller.prototype = {
 +
 +   initialize: function(liveGrid, viewPort) {
 +      this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
 +      this.liveGrid = liveGrid;
 +      this.metaData = liveGrid.metaData;
 +      this.createScrollBar();
 +      this.scrollTimeout = null;
 +      this.lastScrollPos = 0;
 +      this.viewPort = viewPort;
 +      this.rows = new Array();
 +   },
 +
 +   isUnPlugged: function() {
 +      return this.scrollerDiv.onscroll == null;
 +   },
 +
 +   plugin: function() {
 +      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
 +   },
 +
 +   unplug: function() {
 +      this.scrollerDiv.onscroll = null;
 +   },
 +
 +   sizeIEHeaderHack: function() {
 +      if ( !this.isIE ) return;
 +      var headerTable = $(this.liveGrid.tableId + "_header");
 +      if ( headerTable )
 +         headerTable.rows[0].cells[0].style.width =
 +            (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
 +   },
 +
 +   createScrollBar: function() {
 +      var visibleHeight = this.liveGrid.viewPort.visibleHeight();
 +      // create the outer div...
 +      this.scrollerDiv  = document.createElement("div");
 +      var scrollerStyle = this.scrollerDiv.style;
 +      scrollerStyle.borderRight = this.liveGrid.options.scrollerBorderRight;
 +      scrollerStyle.position    = "relative";
 +      scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
 +      scrollerStyle.width       = "19px";
 +      scrollerStyle.height      = visibleHeight + "px";
 +      scrollerStyle.overflow    = "auto";
 +
 +      // create the inner div...
 +      this.heightDiv = document.createElement("div");
 +      this.heightDiv.style.width  = "1px";
 +
 +      this.heightDiv.style.height = parseInt(visibleHeight *
 +                        this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
 +      this.scrollerDiv.appendChild(this.heightDiv);
 +      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
 +
 +     var table = this.liveGrid.table;
 +     table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling );
 +  	  var eventName = this.isIE ? "mousewheel" : "DOMMouseScroll";
 +	  Event.observe(table, eventName,
 +	                function(evt) {
 +	                   if (evt.wheelDelta>=0 || evt.detail < 0) //wheel-up
 +	                      this.scrollerDiv.scrollTop -= (2*this.viewPort.rowHeight);
 +	                   else
 +	                      this.scrollerDiv.scrollTop += (2*this.viewPort.rowHeight);
 +	                   this.handleScroll(false);
 +	                }.bindAsEventListener(this),
 +	                false);
 +     },
 +
 +   updateSize: function() {
 +      var table = this.liveGrid.table;
 +      var visibleHeight = this.viewPort.visibleHeight();
 +      this.heightDiv.style.height = parseInt(visibleHeight *
 +                                  this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
 +   },
 +
 +   rowToPixel: function(rowOffset) {
 +      return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
 +   },
 +
 +   moveScroll: function(rowOffset) {
 +      this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset);
 +      if ( this.metaData.options.onscroll )
 +         this.metaData.options.onscroll( this.liveGrid, rowOffset );
 +   },
 +
 +   handleScroll: function() {
 +     if ( this.scrollTimeout )
 +         clearTimeout( this.scrollTimeout );
 +
 +    var scrollDiff = this.lastScrollPos-this.scrollerDiv.scrollTop;
 +    if (scrollDiff != 0.00) {
 +       var r = this.scrollerDiv.scrollTop % this.viewPort.rowHeight;
 +       if (r != 0) {
 +          this.unplug();
 +          if ( scrollDiff < 0 ) {
 +             this.scrollerDiv.scrollTop += (this.viewPort.rowHeight-r);
 +          } else {
 +             this.scrollerDiv.scrollTop -= r;
 +          }
 +          this.plugin();
 +       }
 +    }
 +    var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
 +    this.liveGrid.requestContentRefresh(contentOffset);
 +    this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
 +
 +    if ( this.metaData.options.onscroll )
 +       this.metaData.options.onscroll( this.liveGrid, contentOffset );
 +
 +    this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
 +    this.lastScrollPos = this.scrollerDiv.scrollTop;
 +
 +   },
 +
 +   scrollIdle: function() {
 +      if ( this.metaData.options.onscrollidle )
 +         this.metaData.options.onscrollidle();
 +   }
 +};
 +
 +// Rico.LiveGridBuffer -----------------------------------------------------
 +
 +Rico.LiveGridBuffer = Class.create();
 +
 +Rico.LiveGridBuffer.prototype = {
 +
 +   initialize: function(metaData, viewPort) {
 +      this.startPos = 0;
 +      this.size     = 0;
 +      this.metaData = metaData;
 +      this.rows     = new Array();
 +      this.updateInProgress = false;
 +      this.viewPort = viewPort;
 +      this.maxBufferSize = metaData.getLargeBufferSize() * 2;
 +      this.maxFetchSize = metaData.getLargeBufferSize();
 +      this.lastOffset = 0;
 +   },
 +
 +   getBlankRow: function() {
 +      if (!this.blankRow ) {
 +         this.blankRow = new Array();
 +         for ( var i=0; i < this.metaData.columnCount ; i++ )
 +            this.blankRow[i] = " ";
 +     }
 +     return this.blankRow;
 +   },
 +
 +   loadRows: function(ajaxResponse) {
 +      var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
 +      this.updateUI = rowsElement.getAttribute("update_ui") == "true"
 +      var newRows = new Array()
 +      var trs = rowsElement.getElementsByTagName("tr");
 +      for ( var i=0 ; i < trs.length; i++ ) {
 +         var row = newRows[i] = new Array();
 +         var cells = trs[i].getElementsByTagName("td");
 +         for ( var j=0; j < cells.length ; j++ ) {
 +            var cell = cells[j];
 +            var convertSpaces = cell.getAttribute("convert_spaces") == "true";
 +            var cellContent = RicoUtil.getContentAsString(cell);
 +            row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
 +            if (!row[j])
 +               row[j] = ' ';
 +         }
 +      }
 +      return newRows;
 +   },
 +
 +   update: function(ajaxResponse, start) {
 +     var newRows = this.loadRows(ajaxResponse);
 +      if (this.rows.length == 0) { // initial load
 +         this.rows = newRows;
 +         this.size = this.rows.length;
 +         this.startPos = start;
 +         return;
 +      }
 +      if (start > this.startPos) { //appending
 +         if (this.startPos + this.rows.length < start) {
 +            this.rows =  newRows;
 +            this.startPos = start;//
 +         } else {
 +              this.rows = this.rows.concat( newRows.slice(0, newRows.length));
 +            if (this.rows.length > this.maxBufferSize) {
 +               var fullSize = this.rows.length;
 +               this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
 +               this.startPos = this.startPos +  (fullSize - this.rows.length);
 +            }
 +         }
 +      } else { //prepending
 +         if (start + newRows.length < this.startPos) {
 +            this.rows =  newRows;
 +         } else {
 +            this.rows = newRows.slice(0, this.startPos).concat(this.rows);
 +            if (this.rows.length > this.maxBufferSize)
 +               this.rows = this.rows.slice(0, this.maxBufferSize)
 +         }
 +         this.startPos =  start;
 +      }
 +      this.size = this.rows.length;
 +   },
 +
 +   clear: function() {
 +      this.rows = new Array();
 +      this.startPos = 0;
 +      this.size = 0;
 +   },
 +
 +   isOverlapping: function(start, size) {
 +      return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0)
 +   },
 +
 +   isInRange: function(position) {
 +      return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos());
 +             //&& this.size()  != 0;
 +   },
 +
 +   isNearingTopLimit: function(position) {
 +      return position - this.startPos < this.metaData.getLimitTolerance();
 +   },
 +
 +   endPos: function() {
 +      return this.startPos + this.rows.length;
 +   },
 +
 +   isNearingBottomLimit: function(position) {
 +      return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance();
 +   },
 +
 +   isAtTop: function() {
 +      return this.startPos == 0;
 +   },
 +
 +   isAtBottom: function() {
 +      return this.endPos() == this.metaData.getTotalRows();
 +   },
 +
 +   isNearingLimit: function(position) {
 +      return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
 +             ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
 +   },
 +
 +   getFetchSize: function(offset) {
 +      var adjustedOffset = this.getFetchOffset(offset);
 +      var adjustedSize = 0;
 +      if (adjustedOffset >= this.startPos) { //apending
 +         var endFetchOffset = this.maxFetchSize  + adjustedOffset;
 +         if (endFetchOffset > this.metaData.totalRows)
 +            endFetchOffset = this.metaData.totalRows;
 +         adjustedSize = endFetchOffset - adjustedOffset;
 +			if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize){
 +			   adjustedSize = this.maxFetchSize;
 +			}
 +      } else {//prepending
 +         var adjustedSize = this.startPos - adjustedOffset;
 +         if (adjustedSize > this.maxFetchSize)
 +            adjustedSize = this.maxFetchSize;
 +      }
 +      return adjustedSize;
 +   },
 +
 +   getFetchOffset: function(offset) {
 +      var adjustedOffset = offset;
 +      if (offset > this.startPos)  //apending
 +         adjustedOffset = (offset > this.endPos()) ? offset :  this.endPos();
 +      else { //prepending
 +         if (offset + this.maxFetchSize >= this.startPos) {
 +            var adjustedOffset = this.startPos - this.maxFetchSize;
 +            if (adjustedOffset < 0)
 +               adjustedOffset = 0;
 +         }
 +      }
 +      this.lastOffset = adjustedOffset;
 +      return adjustedOffset;
 +   },
 +
 +   getRows: function(start, count) {
 +      var begPos = start - this.startPos
 +      var endPos = begPos + count
 +
 +      // er? need more data...
 +      if ( endPos > this.size )
 +         endPos = this.size
 +
 +      var results = new Array()
 +      var index = 0;
 +      for ( var i=begPos ; i < endPos; i++ ) {
 +         results[index++] = this.rows[i]
 +      }
 +      return results
 +   },
 +
 +   convertSpaces: function(s) {
 +      return s.split(" ").join(" ");
 +   }
 +
 +};
 +
 +
 +//Rico.GridViewPort --------------------------------------------------
 +Rico.GridViewPort = Class.create();
 +
 +Rico.GridViewPort.prototype = {
 +
 +   initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) {
 +      this.lastDisplayedStartPos = 0;
 +      this.div = table.parentNode;
 +      this.table = table
 +      this.rowHeight = rowHeight;
 +      this.div.style.height = this.rowHeight * visibleRows;
 +      this.div.style.overflow = "hidden";
 +      this.buffer = buffer;
 +      this.liveGrid = liveGrid;
 +      this.visibleRows = visibleRows + 1;
 +      this.lastPixelOffset = 0;
 +      this.startPos = 0;
 +   },
 +
 +   populateRow: function(htmlRow, row) {
 +      for (var j=0; j < row.length; j++) {
 +         htmlRow.cells[j].innerHTML = row[j]
 +      }
 +   },
 +
 +   bufferChanged: function() {
 +      this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight));
 +   },
 +
 +   clearRows: function() {
 +      if (!this.isBlank) {
 +         this.liveGrid.table.className = this.liveGrid.options.loadingClass;
 +         for (var i=0; i < this.visibleRows; i++)
 +            this.populateRow(this.table.rows[i], this.buffer.getBlankRow());
 +         this.isBlank = true;
 +      }
 +   },
 +
 +   clearContents: function() {
 +      this.clearRows();
 +      this.scrollTo(0);
 +      this.startPos = 0;
 +      this.lastStartPos = -1;
 +   },
 +
 +   refreshContents: function(startPos) {
 +      if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
 +         return;
 +      }
 +      if ((startPos + this.visibleRows < this.buffer.startPos)
 +          || (this.buffer.startPos + this.buffer.size < startPos)
 +          || (this.buffer.size == 0)) {
 +         this.clearRows();
 +         return;
 +      }
 +      this.isBlank = false;
 +      var viewPrecedesBuffer = this.buffer.startPos > startPos
 +      var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
 +      var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows)
 +                                 ? this.buffer.startPos + this.buffer.size
 +                                 : startPos + this.visibleRows;
 +      var rowSize = contentEndPos - contentStartPos;
 +      var rows = this.buffer.getRows(contentStartPos, rowSize );
 +      var blankSize = this.visibleRows - rowSize;
 +      var blankOffset = viewPrecedesBuffer ? 0: rowSize;
 +      var contentOffset = viewPrecedesBuffer ? blankSize: 0;
 +
 +      for (var i=0; i < rows.length; i++) {//initialize what we have
 +        this.populateRow(this.table.rows[i + contentOffset], rows[i]);
 +      }
 +      for (var i=0; i < blankSize; i++) {// blank out the rest
 +        this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow());
 +      }
 +      this.isPartialBlank = blankSize > 0;
 +      this.lastRowPos = startPos;
 +
 +       this.liveGrid.table.className = this.liveGrid.options.tableClass;
 +       // Check if user has set a onRefreshComplete function
 +       var onRefreshComplete = this.liveGrid.options.onRefreshComplete;
 +       if (onRefreshComplete != null)
 +           onRefreshComplete();
 +   },
 +
 +   scrollTo: function(pixelOffset) {
 +      if (this.lastPixelOffset == pixelOffset)
 +         return;
 +
 +      this.refreshContents(parseInt(pixelOffset / this.rowHeight))
 +      this.div.scrollTop = pixelOffset % this.rowHeight
 +
 +      this.lastPixelOffset = pixelOffset;
 +   },
 +
 +   visibleHeight: function() {
 +      return parseInt(RicoUtil.getElementsComputedStyle(this.div, 'height'));
 +   }
 +
 +};
 +
 +
 +Rico.LiveGridRequest = Class.create();
 +Rico.LiveGridRequest.prototype = {
 +   initialize: function( requestOffset, options ) {
 +      this.requestOffset = requestOffset;
 +   }
 +};
 +
 +// Rico.LiveGrid -----------------------------------------------------
 +
 +Rico.LiveGrid = Class.create();
 +
 +Rico.LiveGrid.prototype = {
 +
 +   initialize: function( tableId, visibleRows, totalRows, url, options, ajaxOptions ) {
 +
 +     this.options = {
 +                tableClass:           $(tableId).className,
 +                loadingClass:         $(tableId).className,
 +                scrollerBorderRight: '1px solid #ababab',
 +                bufferTimeout:        20000,
 +                sortAscendImg:        'images/sort_asc.gif',
 +                sortDescendImg:       'images/sort_desc.gif',
 +                sortImageWidth:       9,
 +                sortImageHeight:      5,
 +                ajaxSortURLParms:     [],
 +                onRefreshComplete:    null,
 +                requestParameters:    null,
 +                inlineStyles:         true
 +                };
 +      Object.extend(this.options, options || {});
 +
 +      this.ajaxOptions = {parameters: null};
 +      Object.extend(this.ajaxOptions, ajaxOptions || {});
 +
 +      this.tableId     = tableId;
 +      this.table       = $(tableId);
 +
 +      this.addLiveGridHtml();
 +
 +      var columnCount  = this.table.rows[0].cells.length;
 +      this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options);
 +      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
 +
 +      var rowCount = this.table.rows.length;
 +      this.viewPort =  new Rico.GridViewPort(this.table,
 +                                            this.table.offsetHeight/rowCount,
 +                                            visibleRows,
 +                                            this.buffer, this);
 +      this.scroller    = new Rico.LiveGridScroller(this,this.viewPort);
 +      this.options.sortHandler = this.sortHandler.bind(this);
 +
 +      if ( $(tableId + '_header') )
 +         this.sort = new Rico.LiveGridSort(tableId + '_header', this.options)
 +
 +      this.processingRequest = null;
 +      this.unprocessedRequest = null;
 +
 +      this.initAjax(url);
 +      if ( this.options.prefetchBuffer || this.options.prefetchOffset > 0) {
 +         var offset = 0;
 +         if (this.options.offset ) {
 +            offset = this.options.offset;
 +            this.scroller.moveScroll(offset);
 +            this.viewPort.scrollTo(this.scroller.rowToPixel(offset));
 +         }
 +         if (this.options.sortCol) {
 +             this.sortCol = options.sortCol;
 +             this.sortDir = options.sortDir;
 +         }
 +         this.requestContentRefresh(offset);
 +      }
 +   },
 +
 +   addLiveGridHtml: function() {
 +     // Check to see if need to create a header table.
 +     if (this.table.getElementsByTagName("thead").length > 0){
 +       // Create Table this.tableId+'_header'
 +       var tableHeader = this.table.cloneNode(true);
 +       tableHeader.setAttribute('id', this.tableId+'_header');
 +       tableHeader.setAttribute('class', this.table.className+'_header');
 +
 +       // Clean up and insert
 +       for( var i = 0; i < tableHeader.tBodies.length; i++ )
 +       tableHeader.removeChild(tableHeader.tBodies[i]);
 +       this.table.deleteTHead();
 +       this.table.parentNode.insertBefore(tableHeader,this.table);
 +     }
 +
 +    new Insertion.Before(this.table, "<div id='"+this.tableId+"_container'></div>");
 +    this.table.previousSibling.appendChild(this.table);
 +    new Insertion.Before(this.table,"<div id='"+this.tableId+"_viewport' style='float:left;'></div>");
 +    this.table.previousSibling.appendChild(this.table);
 +   },
 +
 +   resetContents: function() {
 +      this.scroller.moveScroll(0);
 +      this.buffer.clear();
 +      this.viewPort.clearContents();
 +   },
 +
 +   sortHandler: function(column) {
 +      this.sortCol = column.name;
 +      this.sortDir = column.currentSort;
 +
 +      this.resetContents();
 +      this.requestContentRefresh(0)
 +   },
 +
 +   setTotalRows: function( newTotalRows ) {
 +      this.resetContents();
 +      this.metaData.setTotalRows(newTotalRows);
 +      this.scroller.updateSize();
 +   },
 +
 +   initAjax: function(url) {
 +      ajaxEngine.registerRequest( this.tableId + '_request', url );
 +      ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
 +   },
 +
 +   invokeAjax: function() {
 +   },
 +
 +   handleTimedOut: function() {
 +      //server did not respond in 4 seconds... assume that there could have been
 +      //an error or something, and allow requests to be processed again...
 +      this.processingRequest = null;
 +      this.processQueuedRequest();
 +   },
 +
 +   fetchBuffer: function(offset) {
 +      if ( this.buffer.isInRange(offset) &&
 +         !this.buffer.isNearingLimit(offset)) {
 +         return;
 +         }
 +      if (this.processingRequest) {
 +          this.unprocessedRequest = new Rico.LiveGridRequest(offset);
 +         return;
 +      }
 +      var bufferStartPos = this.buffer.getFetchOffset(offset);
 +      this.processingRequest = new Rico.LiveGridRequest(offset);
 +      this.processingRequest.bufferOffset = bufferStartPos;
 +      var fetchSize = this.buffer.getFetchSize(offset);
 +      var partialLoaded = false;
 +
 +      var queryString
 +      if (this.options.requestParameters)
 +         queryString = this._createQueryString(this.options.requestParameters, 0);
 +
 +        queryString = (queryString == null) ? '' : queryString+'&';
 +        queryString  = queryString+'id='+this.tableId+'&page_size='+fetchSize+'&offset='+bufferStartPos;
 +        if (this.sortCol)
 +            queryString = queryString+'&sort_col='+escape(this.sortCol)+'&sort_dir='+this.sortDir;
 +
 +        this.ajaxOptions.parameters = queryString;
 +
 +       ajaxEngine.sendRequest( this.tableId + '_request', this.ajaxOptions );
 +
 +       this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
 +
 +   },
 +
 +   setRequestParams: function() {
 +      this.options.requestParameters = [];
 +      for ( var i=0 ; i < arguments.length ; i++ )
 +         this.options.requestParameters[i] = arguments[i];
 +   },
 +
 +   requestContentRefresh: function(contentOffset) {
 +      this.fetchBuffer(contentOffset);
 +   },
 +
 +   ajaxUpdate: function(ajaxResponse) {
 +      try {
 +         clearTimeout( this.timeoutHandler );
 +         this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
 +         this.viewPort.bufferChanged();
 +      }
 +      catch(err) {}
 +      finally {this.processingRequest = null; }
 +      this.processQueuedRequest();
 +   },
 +
 +   _createQueryString: function( theArgs, offset ) {
 +      var queryString = ""
 +      if (!theArgs)
 +          return queryString;
 +
 +      for ( var i = offset ; i < theArgs.length ; i++ ) {
 +          if ( i != offset )
 +            queryString += "&";
 +
 +          var anArg = theArgs[i];
 +
 +          if ( anArg.name != undefined && anArg.value != undefined ) {
 +            queryString += anArg.name +  "=" + escape(anArg.value);
 +          }
 +          else {
 +             var ePos  = anArg.indexOf('=');
 +             var argName  = anArg.substring( 0, ePos );
 +             var argValue = anArg.substring( ePos + 1 );
 +             queryString += argName + "=" + escape(argValue);
 +          }
 +      }
 +      return queryString;
 +   },
 +
 +   processQueuedRequest: function() {
 +      if (this.unprocessedRequest != null) {
 +         this.requestContentRefresh(this.unprocessedRequest.requestOffset);
 +         this.unprocessedRequest = null
 +      }
 +   }
 +};
 +
 +
 +//-------------------- ricoLiveGridSort.js
 +Rico.LiveGridSort = Class.create();
 +
 +Rico.LiveGridSort.prototype = {
 +
 +   initialize: function(headerTableId, options) {
 +      this.headerTableId = headerTableId;
 +      this.headerTable   = $(headerTableId);
 +      this.options = options;
 +      this.setOptions();
 +      this.applySortBehavior();
 +
 +      if ( this.options.sortCol ) {
 +         this.setSortUI( this.options.sortCol, this.options.sortDir );
 +      }
 +   },
 +
 +   setSortUI: function( columnName, sortDirection ) {
 +      var cols = this.options.columns;
 +      for ( var i = 0 ; i < cols.length ; i++ ) {
 +         if ( cols[i].name == columnName ) {
 +            this.setColumnSort(i, sortDirection);
 +            break;
 +         }
 +      }
 +   },
 +
 +   setOptions: function() {
 +      // preload the images...
 +      new Image().src = this.options.sortAscendImg;
 +      new Image().src = this.options.sortDescendImg;
 +
 +      this.sort = this.options.sortHandler;
 +      if ( !this.options.columns )
 +         this.options.columns = this.introspectForColumnInfo();
 +      else {
 +         // allow client to pass { columns: [ ["a", true], ["b", false] ] }
 +         // and convert to an array of Rico.TableColumn objs...
 +         this.options.columns = this.convertToTableColumns(this.options.columns);
 +      }
 +   },
 +
 +   applySortBehavior: function() {
 +      var headerRow   = this.headerTable.rows[0];
 +      var headerCells = headerRow.cells;
 +      for ( var i = 0 ; i < headerCells.length ; i++ ) {
 +         this.addSortBehaviorToColumn( i, headerCells[i] );
 +      }
 +   },
 +
 +   addSortBehaviorToColumn: function( n, cell ) {
 +      if ( this.options.columns[n].isSortable() ) {
 +         cell.id            = this.headerTableId + '_' + n;
 +         cell.style.cursor  = 'pointer';
 +         cell.onclick       = this.headerCellClicked.bindAsEventListener(this);
 +         cell.innerHTML     = cell.innerHTML + '<span id="' + this.headerTableId + '_img_' + n + '">'
 +                           + '   </span>';
 +      }
 +   },
 +
 +   // event handler....
 +   headerCellClicked: function(evt) {
 +      var eventTarget = evt.target ? evt.target : evt.srcElement;
 +      var cellId = eventTarget.id;
 +      var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 ));
 +      var sortedColumnIndex = this.getSortedColumnIndex();
 +      if ( sortedColumnIndex != -1 ) {
 +         if ( sortedColumnIndex != columnNumber ) {
 +            this.removeColumnSort(sortedColumnIndex);
 +            this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
 +         }
 +         else
 +            this.toggleColumnSort(sortedColumnIndex);
 +      }
 +      else
 +         this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
 +
 +      if (this.options.sortHandler) {
 +         this.options.sortHandler(this.options.columns[columnNumber]);
 +      }
 +   },
 +
 +   removeColumnSort: function(n) {
 +      this.options.columns[n].setUnsorted();
 +      this.setSortImage(n);
 +   },
 +
 +   setColumnSort: function(n, direction) {
 +      this.options.columns[n].setSorted(direction);
 +      this.setSortImage(n);
 +   },
 +
 +   toggleColumnSort: function(n) {
 +      this.options.columns[n].toggleSort();
 +      this.setSortImage(n);
 +   },
 +
 +   setSortImage: function(n) {
 +      var sortDirection = this.options.columns[n].getSortDirection();
 +
 +      var sortImageSpan = $( this.headerTableId + '_img_' + n );
 +      if ( sortDirection == Rico.TableColumn.UNSORTED )
 +         sortImageSpan.innerHTML = '  ';
 +      else if ( sortDirection == Rico.TableColumn.SORT_ASC )
 +         sortImageSpan.innerHTML = '  <img width="'  + this.options.sortImageWidth    + '" ' +
 +                                                     'height="'+ this.options.sortImageHeight   + '" ' +
 +                                                     'src="'   + this.options.sortAscendImg + '"/>';
 +      else if ( sortDirection == Rico.TableColumn.SORT_DESC )
 +         sortImageSpan.innerHTML = '  <img width="'  + this.options.sortImageWidth    + '" ' +
 +                                                     'height="'+ this.options.sortImageHeight   + '" ' +
 +                                                     'src="'   + this.options.sortDescendImg + '"/>';
 +   },
 +
 +   getSortedColumnIndex: function() {
 +      var cols = this.options.columns;
 +      for ( var i = 0 ; i < cols.length ; i++ ) {
 +         if ( cols[i].isSorted() )
 +            return i;
 +      }
 +
 +      return -1;
 +   },
 +
 +   introspectForColumnInfo: function() {
 +      var columns = new Array();
 +      var headerRow   = this.headerTable.rows[0];
 +      var headerCells = headerRow.cells;
 +      for ( var i = 0 ; i < headerCells.length ; i++ )
 +         columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) );
 +      return columns;
 +   },
 +
 +   convertToTableColumns: function(cols) {
 +      var columns = new Array();
 +      for ( var i = 0 ; i < cols.length ; i++ )
 +         columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) );
 +      return columns;
 +   },
 +
 +   deriveColumnNameFromCell: function(cell,columnNumber) {
 +      var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent;
 +      return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber;
 +   }
 +};
 +
 +Rico.TableColumn = Class.create();
 +
 +Rico.TableColumn.UNSORTED  = 0;
 +Rico.TableColumn.SORT_ASC  = "ASC";
 +Rico.TableColumn.SORT_DESC = "DESC";
 +
 +Rico.TableColumn.prototype = {
 +   initialize: function(name, sortable) {
 +      this.name        = name;
 +      this.sortable    = sortable;
 +      this.currentSort = Rico.TableColumn.UNSORTED;
 +   },
 +
 +   isSortable: function() {
 +      return this.sortable;
 +   },
 +
 +   isSorted: function() {
 +      return this.currentSort != Rico.TableColumn.UNSORTED;
 +   },
 +
 +   getSortDirection: function() {
 +      return this.currentSort;
 +   },
 +
 +   toggleSort: function() {
 +      if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC )
 +         this.currentSort = Rico.TableColumn.SORT_ASC;
 +      else if ( this.currentSort == Rico.TableColumn.SORT_ASC )
 +         this.currentSort = Rico.TableColumn.SORT_DESC;
 +   },
 +
 +   setUnsorted: function(direction) {
 +      this.setSorted(Rico.TableColumn.UNSORTED);
 +   },
 +
 +   setSorted: function(direction) {
 +      // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC...
 +      this.currentSort = direction;
 +   }
 +
 +};
 +
 +
 +//-------------------- ricoUtil.js
 +Rico.ArrayExtensions = new Array();
 +
 +if (Object.prototype.extend) {
 +   // in prototype.js...
 +   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
 +}else{
 +  Object.prototype.extend = function(object) {
 +    return Object.extend.apply(this, [this, object]);
 +  }
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
 +}
 +
 +if (Array.prototype.push) {
 +   // in prototype.js...
 +   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
 +}
 +
 +if (!Array.prototype.remove) {
 +   Array.prototype.remove = function(dx) {
 +      if( isNaN(dx) || dx > this.length )
 +         return false;
 +      for( var i=0,n=0; i<this.length; i++ )
 +         if( i != dx )
 +            this[n++]=this[i];
 +      this.length-=1;
 +   };
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
 +}
 +
 +if (!Array.prototype.removeItem) {
 +   Array.prototype.removeItem = function(item) {
 +      for ( var i = 0 ; i < this.length ; i++ )
 +         if ( this[i] == item ) {
 +            this.remove(i);
 +            break;
 +         }
 +   };
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
 +}
 +
 +if (!Array.prototype.indices) {
 +   Array.prototype.indices = function() {
 +      var indexArray = new Array();
 +      for ( index in this ) {
 +         var ignoreThis = false;
 +         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
 +            if ( this[index] == Rico.ArrayExtensions[i] ) {
 +               ignoreThis = true;
 +               break;
 +            }
 +         }
 +         if ( !ignoreThis )
 +            indexArray[ indexArray.length ] = index;
 +      }
 +      return indexArray;
 +   }
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
 +}
 +
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.unique;
 +  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.inArray;
 +
 +
 +// Create the loadXML method and xml getter for Mozilla
 +if ( window.DOMParser &&
 +	  window.XMLSerializer &&
 +	  window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
 +
 +   if (!Document.prototype.loadXML) {
 +      Document.prototype.loadXML = function (s) {
 +         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
 +         while (this.hasChildNodes())
 +            this.removeChild(this.lastChild);
 +
 +         for (var i = 0; i < doc2.childNodes.length; i++) {
 +            this.appendChild(this.importNode(doc2.childNodes[i], true));
 +         }
 +      };
 +	}
 +
 +	Document.prototype.__defineGetter__( "xml",
 +	   function () {
 +		   return (new XMLSerializer()).serializeToString(this);
 +	   }
 +	 );
 +}
 +
 +document.getElementsByTagAndClassName = function(tagName, className) {
 +  if ( tagName == null )
 +     tagName = '*';
 +
 +  var children = document.getElementsByTagName(tagName) || document.all;
 +  var elements = new Array();
 +
 +  if ( className == null )
 +    return children;
 +
 +  for (var i = 0; i < children.length; i++) {
 +    var child = children[i];
 +    var classNames = child.className.split(' ');
 +    for (var j = 0; j < classNames.length; j++) {
 +      if (classNames[j] == className) {
 +        elements.push(child);
 +        break;
 +      }
 +    }
 +  }
 +
 +  return elements;
 +}
 +
 +var RicoUtil = {
 +
 +   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
 +      if ( arguments.length == 2 )
 +         mozillaEquivalentCSS = cssProperty;
 +
 +      var el = $(htmlElement);
 +      if ( el.currentStyle )
 +         return el.currentStyle[cssProperty];
 +      else
 +         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
 +   },
 +
 +   createXmlDocument : function() {
 +      if (document.implementation && document.implementation.createDocument) {
 +         var doc = document.implementation.createDocument("", "", null);
 +
 +         if (doc.readyState == null) {
 +            doc.readyState = 1;
 +            doc.addEventListener("load", function () {
 +               doc.readyState = 4;
 +               if (typeof doc.onreadystatechange == "function")
 +                  doc.onreadystatechange();
 +            }, false);
 +         }
 +
 +         return doc;
 +      }
 +
 +      if (window.ActiveXObject)
 +          return Try.these(
 +            function() { return new ActiveXObject('MSXML2.DomDocument')   },
 +            function() { return new ActiveXObject('Microsoft.DomDocument')},
 +            function() { return new ActiveXObject('MSXML.DomDocument')    },
 +            function() { return new ActiveXObject('MSXML3.DomDocument')   }
 +          ) || false;
 +
 +      return null;
 +   },
 +
 +   getContentAsString: function( parentNode ) {
 +      return parentNode.xml != undefined ?
 +         this._getContentAsStringIE(parentNode) :
 +         this._getContentAsStringMozilla(parentNode);
 +   },
 +
 +  _getContentAsStringIE: function(parentNode) {
 +     var contentStr = "";
 +     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
 +         var n = parentNode.childNodes[i];
 +         if (n.nodeType == 4) {
 +             contentStr += n.nodeValue;
 +         }
 +         else {
 +           contentStr += n.xml;
 +       }
 +     }
 +     return contentStr;
 +  },
 +
 +  _getContentAsStringMozilla: function(parentNode) {
 +     var xmlSerializer = new XMLSerializer();
 +     var contentStr = "";
 +     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
 +          var n = parentNode.childNodes[i];
 +          if (n.nodeType == 4) { // CDATA node
 +              contentStr += n.nodeValue;
 +          }
 +          else {
 +            contentStr += xmlSerializer.serializeToString(n);
 +        }
 +     }
 +     return contentStr;
 +  },
 +
 +   toViewportPosition: function(element) {
 +      return this._toAbsolute(element,true);
 +   },
 +
 +   toDocumentPosition: function(element) {
 +      return this._toAbsolute(element,false);
 +   },
 +
 +   /**
 +    *  Compute the elements position in terms of the window viewport
 +    *  so that it can be compared to the position of the mouse (dnd)
 +    *  This is additions of all the offsetTop,offsetLeft values up the
 +    *  offsetParent hierarchy, ...taking into account any scrollTop,
 +    *  scrollLeft values along the way...
 +    *
 +    * IE has a bug reporting a correct offsetLeft of elements within a
 +    * a relatively positioned parent!!!
 +    **/
 +   _toAbsolute: function(element,accountForDocScroll) {
 +
 +      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
 +         return this._toAbsoluteMozilla(element,accountForDocScroll);
 +
 +      var x = 0;
 +      var y = 0;
 +      var parent = element;
 +      while ( parent ) {
 +
 +         var borderXOffset = 0;
 +         var borderYOffset = 0;
 +         if ( parent != element ) {
 +            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
 +            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
 +            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
 +            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
 +         }
 +
 +         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
 +         y += parent.offsetTop - parent.scrollTop + borderYOffset;
 +         parent = parent.offsetParent;
 +      }
 +
 +      if ( accountForDocScroll ) {
 +         x -= this.docScrollLeft();
 +         y -= this.docScrollTop();
 +      }
 +
 +      return { x:x, y:y };
 +   },
 +
 +   /**
 +    *  Mozilla did not report all of the parents up the hierarchy via the
 +    *  offsetParent property that IE did.  So for the calculation of the
 +    *  offsets we use the offsetParent property, but for the calculation of
 +    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
 +    *  property instead so as to get the scroll offsets...
 +    *
 +    **/
 +   _toAbsoluteMozilla: function(element,accountForDocScroll) {
 +      var x = 0;
 +      var y = 0;
 +      var parent = element;
 +      while ( parent ) {
 +         x += parent.offsetLeft;
 +         y += parent.offsetTop;
 +         parent = parent.offsetParent;
 +      }
 +
 +      parent = element;
 +      while ( parent &&
 +              parent != document.body &&
 +              parent != document.documentElement ) {
 +         if ( parent.scrollLeft  )
 +            x -= parent.scrollLeft;
 +         if ( parent.scrollTop )
 +            y -= parent.scrollTop;
 +         parent = parent.parentNode;
 +      }
 +
 +      if ( accountForDocScroll ) {
 +         x -= this.docScrollLeft();
 +         y -= this.docScrollTop();
 +      }
 +
 +      return { x:x, y:y };
 +   },
 +
 +   docScrollLeft: function() {
 +      if ( window.pageXOffset )
 +         return window.pageXOffset;
 +      else if ( document.documentElement && document.documentElement.scrollLeft )
 +         return document.documentElement.scrollLeft;
 +      else if ( document.body )
 +         return document.body.scrollLeft;
 +      else
 +         return 0;
 +   },
 +
 +   docScrollTop: function() {
 +      if ( window.pageYOffset )
 +         return window.pageYOffset;
 +      else if ( document.documentElement && document.documentElement.scrollTop )
 +         return document.documentElement.scrollTop;
 +      else if ( document.body )
 +         return document.body.scrollTop;
 +      else
 +         return 0;
 +   }
 +
 +}; + +Prado.RicoLiveGrid = Class.create();
 +Prado.RicoLiveGrid.prototype = Object.extend(Rico.LiveGrid.prototype,
 +{
 +	initialize : function(tableId, options)
 +	{
 +	     this.options = {
 +                tableClass:           $(tableId).className || '',
 +                loadingClass:         $(tableId).className || '',
 +                scrollerBorderRight: '1px solid #ababab',
 +                bufferTimeout:        20000,
 +                sortAscendImg:        'images/sort_asc.gif',
 +                sortDescendImg:       'images/sort_desc.gif',
 +                sortImageWidth:       9,
 +                sortImageHeight:      5,
 +                ajaxSortURLParms:     [],
 +                onRefreshComplete:    null,
 +                requestParameters:    null,
 +                inlineStyles:         true,
 +				visibleRows:		  10,
 +				totalRows:			  0,
 +				initialOffset:		  0
 +		};
 +		Object.extend(this.options, options || {});
 +
 +      //this.ajaxOptions = {parameters: null};
 +      //Object.extend(this.ajaxOptions, ajaxOptions || {});
 +
 +      this.tableId     = tableId; 
 +      this.table       = $(tableId);
 +
 +      this.addLiveGridHtml();
 +
 +      var columnCount  = this.table.rows[0].cells.length;
 +      this.metaData    = new Rico.LiveGridMetaData(this.options.visibleRows, this.options.totalRows, columnCount, options);
 +      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
 +
 +      var rowCount = this.table.rows.length;
 +      this.viewPort =  new Rico.GridViewPort(this.table, 
 +                                            this.table.offsetHeight/rowCount,
 +                                            this.options.visibleRows,
 +                                            this.buffer, this);
 +      this.scroller    = new Rico.LiveGridScroller(this,this.viewPort);
 +      this.options.sortHandler = this.sortHandler.bind(this);
 +
 +      if ( $(tableId + '_header') )
 +         this.sort = new Rico.LiveGridSort(tableId + '_header', this.options)
 +
 +      this.processingRequest = null;
 +      this.unprocessedRequest = null;
 +
 +      //this.initAjax(url);
 +      if (this.options.initialOffset >= 0) 
 +	  {
 +         var offset = this.options.initialOffset;
 +            this.scroller.moveScroll(offset);
 +            this.viewPort.scrollTo(this.scroller.rowToPixel(offset));            
 +         if (this.options.sortCol) {
 +             this.sortCol = options.sortCol;
 +             this.sortDir = options.sortDir;
 +         }
 +         var grid = this;
 +		 setTimeout(function(){
 +			 grid.requestContentRefresh(offset);
 +		 },100);
 +      }
 +	},
 +
 +   fetchBuffer: function(offset) 
 +   {
 +      if ( this.buffer.isInRange(offset) &&
 +         !this.buffer.isNearingLimit(offset)) {
 +         return;
 +         }
 +      if (this.processingRequest) {
 +          this.unprocessedRequest = new Rico.LiveGridRequest(offset);
 +         return;
 +      }
 +      var bufferStartPos = this.buffer.getFetchOffset(offset);
 +      this.processingRequest = new Rico.LiveGridRequest(offset);
 +      this.processingRequest.bufferOffset = bufferStartPos;   
 +      var fetchSize = this.buffer.getFetchSize(offset);
 +      var partialLoaded = false;
 +      
 +     // var queryString
 +    //  if (this.options.requestParameters)
 +       //  queryString = this._createQueryString(this.options.requestParameters, 0);
 +		var param = 
 +	   {
 +			'page_size' : fetchSize,
 +			'offset' : bufferStartPos
 +	   };
 +		if(this.sortCol)
 +	   {
 +			Object.extend(param,
 +		   {
 +				'sort_col': this.sortCol,
 +				'sort_dir': this.sortDir
 +			});
 +	   }
 +        /*queryString = (queryString == null) ? '' : queryString+'&';
 +        queryString  = queryString+'id='+this.tableId+'&page_size='+fetchSize+'&offset='+bufferStartPos;
 +        if (this.sortCol)
 +            queryString = queryString+'&sort_col='+escape(this.sortCol)+'&sort_dir='+this.sortDir;
 +
 +        this.ajaxOptions.parameters = queryString;
 +
 +       ajaxEngine.sendRequest( this.tableId + '_request', this.ajaxOptions );
 +		*/
 +		Prado.Callback(this.tableId, param, this.ajaxUpdate.bind(this), this.options);
 +       this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
 +
 +   },
 +
 +   ajaxUpdate: function(result, output) 
 +   {
 +      try {
 +         clearTimeout( this.timeoutHandler );
 +         this.buffer.update(result,this.processingRequest.bufferOffset);
 +         this.viewPort.bufferChanged();
 +      }
 +      catch(err) {}
 +      finally {this.processingRequest = null; }
 +      this.processQueuedRequest();
 +   }
 +});
 +
 +Object.extend(Rico.LiveGridBuffer.prototype,
 +{
 +   update: function(newRows, start) 
 +  {
 +     if (this.rows.length == 0) { // initial load
 +         this.rows = newRows;
 +         this.size = this.rows.length;
 +         this.startPos = start;
 +         return;
 +      }
 +      if (start > this.startPos) { //appending
 +         if (this.startPos + this.rows.length < start) {
 +            this.rows =  newRows;
 +            this.startPos = start;//
 +         } else {
 +              this.rows = this.rows.concat( newRows.slice(0, newRows.length));
 +            if (this.rows.length > this.maxBufferSize) {
 +               var fullSize = this.rows.length;
 +               this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
 +               this.startPos = this.startPos +  (fullSize - this.rows.length);
 +            }
 +         }
 +      } else { //prepending
 +         if (start + newRows.length < this.startPos) {
 +            this.rows =  newRows;
 +         } else {
 +            this.rows = newRows.slice(0, this.startPos).concat(this.rows);
 +            if (this.rows.length > this.maxBufferSize) 
 +               this.rows = this.rows.slice(0, this.maxBufferSize)
 +         }
 +         this.startPos =  start;
 +      }
 +      this.size = this.rows.length;
 +   }
 +});
 +
 +
 +Object.extend(Rico.GridViewPort.prototype,
 +{
 +   populateRow: function(htmlRow, row) 
 +   {
 +	   if(isdef(htmlRow))
 +	   {
 +		  for (var j=0; j < row.length; j++) {
 +			 htmlRow.cells[j].innerHTML = row[j]
 +		  }
 +	   }
 +   }
 +}); + diff --git a/framework/Web/Javascripts/js/debug/validator.js b/framework/Web/Javascripts/js/debug/validator.js new file mode 100644 index 00000000..a77b6f42 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/validator.js @@ -0,0 +1,1279 @@ +
 +/**
 + * Prado client-side javascript validation fascade.
 + *
 + * There are 4 basic classes, Validation, ValidationManager, ValidationSummary
 + * and TBaseValidator, that interact together to perform validation.
 + * The <tt>Prado.Validation</tt> class co-ordinates together the
 + * validation scheme and is responsible for maintaining references
 + * to ValidationManagers.
 + *
 + * The ValidationManager class is responsible for maintaining refereneces
 + * to individual validators, validation summaries and their associated
 + * groupings.
 + *
 + * The ValidationSummary take cares of display the validator error messages
 + * as html output or an alert output.
 + *
 + * The TBaseValidator is the base class for all validators and contains
 + * methods to interact with the actual inputs, data type conversion.
 + *
 + * An instance of ValidationManager must be instantiated first for a
 + * particular form before instantiating validators and summaries.
 + *
 + * Usage example: adding a required field to a text box input with
 + * ID "input1" in a form with ID "form1".
 + * <code>
 + * <script type="text/javascript" src="../prado.js"></script>
 + * <script type="text/javascript" src="../validator.js"></script>
 + * <form id="form1" action="...">
 + * <div>
 + * 	<input type="text" id="input1" />
 + *  <span id="validator1" style="display:none; color:red">*</span>
 + *  <input type="submit text="submit" />
 + * <script type="text/javascript">
 + * new Prado.ValidationManager({FormID : 'form1'});
 + * var options =
 + * {
 + *		ID :				'validator1',
 + *		FormID :			'form1',
 + *		ErrorMessage :		'*',
 + *		ControlToValidate : 'input1'
 + *	}
 + * new Prado.WebUI.TRequiredFieldValidator(options);
 + * new Prado.WebUI.TValidationSummary({ID:'summary1',FormID:'form1'});
 + *
 + * //watch the form onsubmit event, check validators, stop if not valid.
 + * Event.observe("form1", "submit" function(ev)
 + * {
 + * 	 if(Prado.WebUI.Validation.isValid("form1") == false)
 + * 		Event.stop(ev);
 + * });
 + * </script>
 + * </div>
 + * </form>
 + * </code>
 + */
 +Prado.Validation =  Class.create();
 +
 +/**
 + * A global validation manager.
 + * To validate the inputs of a particular form, call
 + * <code>Prado.Validation.validate(formID, groupID)</code>
 + * where <tt>formID</tt> is the HTML form ID, and the optional
 + * <tt>groupID</tt> if present will only validate the validators
 + * in a particular group.
 + */
 +Object.extend(Prado.Validation,
 +{
 +	managers : {},
 +
 +	/**
 +	 * Validate the validators (those that <strong>DO NOT</strong>
 +	 * belong to a particular group) the form specified by the
 +	 * <tt>formID</tt> parameter. If <tt>groupID</tt> is specified
 +	 * then only validators belonging to that group will be validated.
 +	 * @param string ID of the form to validate
 +	 * @param string ID of the group to validate.
 +	 * @param HTMLElement element that calls for validation
 +	 */
 +	validate : function(formID, groupID, invoker)
 +	{
 +		if(this.managers[formID])
 +		{
 +			return this.managers[formID].validate(groupID, invoker);
 +		}
 +		else
 +		{
 +			throw new Error("Form '"+form+"' is not registered with Prado.Validation");
 +		}
 +	},
 +
 +	/**
 +	 * Check if the validators are valid for a particular form (and group).
 +	 * The validators states will not be changed.
 +	 * The <tt>validate</tt> function should be called first.
 +	 * @param string ID of the form to validate
 +	 * @param string ID of the group to validate.
 +	 */
 +	isValid : function(formID, groupID)
 +	{
 +		if(this.managers[formID])
 +			return this.managers[formID].isValid(groupID);
 +		return true;
 +	},
 +
 +	/**
 +	 * Add a new validator to a particular form.
 +	 * @param string the form that the validator belongs.
 +	 * @param object a validator
 +	 * @return object the manager
 +	 */
 +	addValidator : function(formID, validator)
 +	{
 +		if(this.managers[formID])
 +			this.managers[formID].addValidator(validator);
 +		else
 +			throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
 +		return this.managers[formID];
 +	},
 +
 +	/**
 +	 * Add a new validation summary.
 +	 * @param string the form that the validation summary belongs.
 +	 * @param object a validation summary
 +	 * @return object manager
 +	 */
 +	addSummary : function(formID, validator)
 +	{
 +		if(this.managers[formID])
 +			this.managers[formID].addSummary(validator);
 +		else
 +			throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
 +		return this.managers[formID];
 +	}
 +});
 +
 +Prado.ValidationManager = Class.create();
 +/**
 + * Validation manager instances. Manages validators for a particular
 + * HTML form. The manager contains references to all the validators
 + * summaries, and their groupings for a particular form.
 + * Generally, <tt>Prado.Validation</tt> methods should be called rather
 + * than calling directly the ValidationManager.
 + */
 +Prado.ValidationManager.prototype =
 +{
 +	validators : [], // list of validators
 +	summaries : [], // validation summaries
 +	groups : [], // validation groups
 +	options : {},
 +
 +	/**
 +	 * <code>
 +	 * options['FormID']*	The ID of HTML form to manage.
 +	 * </code>
 +	 */
 +	initialize : function(options)
 +	{
 +		this.options = options;
 +		Prado.Validation.managers[options.FormID] = this;
 +	},
 +
 +	/**
 +	 * Validate the validators managed by this validation manager.
 +	 * @param string only validate validators belonging to a group (optional)
 +	 * @param HTMLElement element that calls for validation
 +	 * @return boolean true if all validators are valid, false otherwise.
 +	 */
 +	validate : function(group, invoker)
 +	{
 +		if(group)
 +			return this._validateGroup(group, invoker);
 +		else
 +			return this._validateNonGroup(invoker);
 +	},
 +
 +	/**
 +	 * Validate a particular group of validators.
 +	 * @param string ID of the form
 +	 * @param HTMLElement element that calls for validation
 +	 * @return boolean false if group is not valid, true otherwise.
 +	 */
 +	_validateGroup: function(groupID, invoker)
 +	{
 +		var valid = true;
 +		if(this.groups.include(groupID))
 +		{
 +			this.validators.each(function(validator)
 +			{
 +				if(validator.group == groupID)
 +					valid = valid & validator.validate(invoker);
 +				else
 +					validator.hide();
 +			});
 +		}
 +		this.updateSummary(groupID, true);
 +		return valid;
 +	},
 +
 +	/**
 +	 * Validate validators that doesn't belong to any group.
 +	 * @return boolean false if not valid, true otherwise.
 +	 * @param HTMLElement element that calls for validation
 +	 */
 +	_validateNonGroup : function(invoker)
 +	{
 +		var valid = true;
 +		this.validators.each(function(validator)
 +		{
 +			if(!validator.group)
 +				valid = valid & validator.validate(invoker);
 +			else
 +				validator.hide();
 +		});
 +		this.updateSummary(null, true);
 +		return valid;
 +	},
 +
 +	/**
 +	 * Gets the state of all the validators, true if they are all valid.
 +	 * @return boolean true if the validators are valid.
 +	 */
 +	isValid : function(group)
 +	{
 +		if(group)
 +			return this._isValidGroup(group);
 +		else
 +			return this._isValidNonGroup();
 +	},
 +
 +	/**
 +	 * @return boolean true if all validators not belonging to a group are valid.
 +	 */
 +	_isValidNonGroup : function()
 +	{
 +		var valid = true;
 +		this.validators.each(function(validator)
 +		{
 +			if(!validator.group)
 +				valid = valid & validator.isValid;
 +		});
 +		return valid;
 +	},
 +
 +	/**
 +	 * @return boolean true if all validators belonging to the group are valid.
 +	 */
 +	_isValidGroup : function(groupID)
 +	{
 +		var valid = true;
 +		if(this.groups.include(groupID))
 +		{
 +			this.validators.each(function(validator)
 +			{
 +				if(validator.group == groupID)
 +					valid = valid & validator.isValid;
 +			});
 +		}
 +		return valid;
 +	},
 +
 +	/**
 +	 * Add a validator to this manager.
 +	 * @param Prado.WebUI.TBaseValidator a new validator
 +	 */
 +	addValidator : function(validator)
 +	{
 +		this.validators.push(validator);
 +		if(validator.group && !this.groups.include(validator.group))
 +			this.groups.push(validator.group);
 +	},
 +
 +	/**
 +	 * Add a validation summary.
 +	 * @param Prado.WebUI.TValidationSummary validation summary.
 +	 */
 +	addSummary : function(summary)
 +	{
 +		this.summaries.push(summary);
 +	},
 +
 +	/**
 +	 * Gets all validators that belong to a group or that the validator
 +	 * group is null and the validator validation was false.
 +	 * @return array list of validators with error.
 +	 */
 +	getValidatorsWithError : function(group)
 +	{
 +		var validators = this.validators.findAll(function(validator)
 +		{
 +			var notValid = !validator.isValid;
 +			var inGroup = group && validator.group == group;
 +			var noGroup = validator.group == null;
 +			return notValid && (inGroup || noGroup);
 +		});
 +		return validators;
 +	},
 +
 +	/**
 +	 * Update the summary of a particular group.
 +	 * @param string validation group to update.
 +	 */
 +	updateSummary : function(group, refresh)
 +	{
 +		var validators = this.getValidatorsWithError(group);
 +		this.summaries.each(function(summary)
 +		{
 +			var inGroup = group && summary.group == group;
 +			var noGroup = !group && !summary.group;
 +			if(inGroup || noGroup)
 +				summary.updateSummary(validators, refresh);
 +			else
 +				summary.hideSummary(true);
 +		});
 +	}
 +};
 +
 +/**
 + * TValidationSummary displays a summary of validation errors inline on a Web page,
 + * in a message box, or both. By default, a validation summary will collect
 + * <tt>ErrorMessage</tt> of all failed validators on the page. If
 + * <tt>ValidationGroup</tt> is not empty, only those validators who belong
 + * to the group will show their error messages in the summary.
 + *
 + * The summary can be displayed as a list, as a bulleted list, or as a single
 + * paragraph based on the <tt>DisplayMode</tt> option.
 + * The messages shown can be prefixed with <tt>HeaderText</tt>.
 + *
 + * The summary can be displayed on the Web page and in a message box by setting
 + * the <tt>ShowSummary</tt> and <tt>ShowMessageBox</tt>
 + * options, respectively.
 + */
 +Prado.WebUI.TValidationSummary = Class.create();
 +Prado.WebUI.TValidationSummary.prototype =
 +{
 +	group : null,
 +	options : {},
 +	visible : false,
 +	messages : null,
 +
 +	/**
 +	 * <code>
 +	 * options['ID']*				Validation summary ID, i.e., an HTML element ID
 +	 * options['FormID']*			HTML form that this summary belongs.
 +	 * options['ShowMessageBox']	True to show the summary in an alert box.
 +	 * options['ShowSummary']		True to show the inline summary.
 +	 * options['HeaderText']		Summary header text
 +	 * options['DisplayMode']		Summary display style, 'BulletList', 'List', 'SingleParagraph'
 +	 * options['Refresh']			True to update the summary upon validator state change.
 +	 * options['ValidationGroup']	Validation summary group
 +	 * options['Display']			Display mode, 'None', 'Static', 'Dynamic'.
 +	 * options['ScrollToSummary']	True to scroll to the validation summary upon refresh.
 +	 * </code>
 +	 */
 +	initialize : function(options)
 +	{
 +		this.options = options;
 +		this.group = options.ValidationGroup;
 +		this.messages = $(options.ID);
 +		this.visible = this.messages.style.visibility != "hidden"
 +		this.visible = this.visible && this.messages.style.display != "none";
 +		Prado.Validation.addSummary(options.FormID, this);
 +	},
 +
 +	/**
 +	 * Update the validation summary to show the error message from
 +	 * validators that failed validation.
 +	 * @param array list of validators that failed validation.
 +	 * @param boolean update the summary;
 +	 */
 +	updateSummary : function(validators, update)
 +	{
 +		if(validators.length <= 0)
 +		{
 +			if(update || this.options.Refresh != false)
 +			{
 +				return this.hideSummary(validators);
 +			}
 +			return;
 +		}
 +
 +		var refresh = update || this.visible == false || this.options.Refresh != false;
 +
 +		if(this.options.ShowSummary != false && refresh)
 +		{
 +			this.updateHTMLMessages(this.getMessages(validators));
 +			this.showSummary(validators);
 +		}
 +
 +		if(this.options.ScrollToSummary != false && refresh)
 +			window.scrollTo(this.messages.offsetLeft-20, this.messages.offsetTop-20);
 +
 +		if(this.options.ShowMessageBox == true && refresh)
 +		{
 +			this.alertMessages(this.getMessages(validators));
 +			this.visible = true;
 +		}
 +	},
 +
 +	/**
 +	 * Display the validator error messages as inline HTML.
 +	 */
 +	updateHTMLMessages : function(messages)
 +	{
 +		while(this.messages.childNodes.length > 0)
 +			this.messages.removeChild(this.messages.lastChild);
 +		new Insertion.Bottom(this.messages, this.formatSummary(messages));
 +	},
 +
 +	/**
 +	 * Display the validator error messages as an alert box.
 +	 */
 +	alertMessages : function(messages)
 +	{
 +		var text = this.formatMessageBox(messages);
 +		setTimeout(function(){ alert(text); },20);
 +	},
 +
 +	/**
 +	 * @return array list of validator error messages.
 +	 */
 +	getMessages : function(validators)
 +	{
 +		var messages = [];
 +		validators.each(function(validator)
 +		{
 +			var message = validator.getErrorMessage();
 +			if(typeof(message) == 'string' && message.length > 0)
 +				messages.push(message);
 +		})
 +		return messages;
 +	},
 +
 +	/**
 +	 * Hides the validation summary.
 +	 */
 +	hideSummary : function(validators)
 +	{	if(typeof(this.options.OnHideSummary) == "function")
 +		{
 +			this.messages.style.visibility="visible";
 +			this.options.OnHideSummary(this,validators)
 +		}
 +		else
 +		{
 +			this.messages.style.visibility="hidden";
 +			if(this.options.Display == "None" || this.options.Display == "Dynamic")
 +				this.messages.hide();
 +		}
 +		this.visible = false;
 +	},
 +
 +	/**
 +	 * Shows the validation summary.
 +	 */
 +	showSummary : function(validators)
 +	{
 +		this.messages.style.visibility="visible";
 +		if(typeof(this.options.OnShowSummary) == "function")
 +			this.options.OnShowSummary(this,validators);
 +		else
 +			this.messages.show();
 +		this.visible = true;
 +	},
 +
 +	/**
 +	 * Return the format parameters for the summary.
 +	 * @param string format type, "List", "SingleParagraph" or "BulletList"
 +	 * @type array formatting parameters
 +	 */
 +	formats : function(type)
 +	{
 +		switch(type)
 +		{
 +			case "List":
 +				return { header : "<br />", first : "", pre : "", post : "<br />", last : ""};
 +			case "SingleParagraph":
 +				return { header : " ", first : "", pre : "", post : " ", last : "<br />"};
 +			case "BulletList":
 +			default:
 +				return { header : "", first : "<ul>", pre : "<li>", post : "</li>", last : "</ul>"};
 +		}
 +	},
 +
 +	/**
 +	 * Format the message summary.
 +	 * @param array list of error messages.
 +	 * @type string formatted message
 +	 */
 +	formatSummary : function(messages)
 +	{
 +		var format = this.formats(this.options.DisplayMode);
 +		var output = this.options.HeaderText ? this.options.HeaderText + format.header : "";
 +		output += format.first;
 +		messages.each(function(message)
 +		{
 +			output += message.length > 0 ? format.pre + message + format.post : "";
 +		});
 +//		for(var i = 0; i < messages.length; i++)
 +	//		output += (messages[i].length>0) ? format.pre + messages[i] + format.post : "";
 +		output += format.last;
 +		return output;
 +	},
 +	/**
 +	 * Format the message alert box.
 +	 * @param array a list of error messages.
 +	 * @type string format message for alert.
 +	 */
 +	formatMessageBox : function(messages)
 +	{
 +		var output = this.options.HeaderText ? this.options.HeaderText + "\n" : "";
 +		for(var i = 0; i < messages.length; i++)
 +		{
 +			switch(this.options.DisplayMode)
 +			{
 +				case "List":
 +					output += messages[i] + "\n";
 +					break;
 +				case "BulletList":
 +                default:
 +					output += "  - " + messages[i] + "\n";
 +					break;
 +				case "SingleParagraph":
 +					output += messages[i] + " ";
 +					break;
 +			}
 +		}
 +		return output;
 +	}
 +};
 +
 +/**
 + * TBaseValidator serves as the base class for validator controls.
 + *
 + * Validation is performed when a postback control, such as a TButton,
 + * a TLinkButton or a TTextBox (under AutoPostBack mode) is submitting
 + * the page and its <tt>CausesValidation</tt> option is true.
 + * The input control to be validated is specified by <tt>ControlToValidate</tt>
 + * option.
 + */
 +Prado.WebUI.TBaseValidator = Class.create();
 +Prado.WebUI.TBaseValidator.prototype =
 +{
 +	enabled : true,
 +	visible : false,
 +	isValid : true,
 +	options : {},
 +	_isObserving : {},
 +	group : null,
 +	manager : null,
 +	message : null,
 +
 +	/**
 +	 * <code>
 +	 * options['ID']*				Validator ID, e.g. span with message
 +	 * options['FormID']*			HTML form that the validator belongs
 +	 * options['ControlToValidate']*HTML form input to validate
 +	 * options['Display']			Display mode, 'None', 'Static', 'Dynamic'
 +	 * options['ErrorMessage']		Validation error message
 +	 * options['FocusOnError']		True to focus on validation error
 +	 * options['FocusElementID']	Element to focus on error
 +	 * options['ValidationGroup']	Validation group
 +	 * options['ControlCssClass']	Css class to use on the input upon error
 +	 * options['OnValidate']		Function to call immediately after validation
 +	 * options['OnSuccess']			Function to call upon after successful validation
 +	 * options['OnError']			Function to call upon after error in validation.
 +	 * options['ObserveChanges'] 	True to observe changes in input
 +	 * </code>
 +	 */
 +	initialize : function(options)
 +	{
 +	/*	options.OnValidate = options.OnValidate || Prototype.emptyFunction;
 +		options.OnSuccess = options.OnSuccess || Prototype.emptyFunction;
 +		options.OnError = options.OnError || Prototype.emptyFunction;
 +	*/
 +		this.options = options;
 +		this.control = $(options.ControlToValidate);
 +		this.message = $(options.ID);
 +		this.group = options.ValidationGroup;
 +
 +		this.manager = Prado.Validation.addValidator(options.FormID, this);
 +	},
 +
 +	/**
 +	 * @return string validation error message.
 +	 */
 +	getErrorMessage : function()
 +	{
 +		return this.options.ErrorMessage;
 +	},
 +
 +	/**
 +	 * Update the validator span, input CSS class, and focus particular
 +	 * element. Updating the validator control will set the validator
 +	 * <tt>visible</tt> property to true.
 +	 */
 +	updateControl: function()
 +	{
 +		if(this.message)
 +		{
 +			if(this.options.Display == "Dynamic")
 +				this.isValid ? this.message.hide() : this.message.show();
 +			this.message.style.visibility = this.isValid ? "hidden" : "visible";
 +		}
 +
 +		if(this.control)
 +			this.updateControlCssClass(this.control, this.isValid);
 +
 +		if(this.options.FocusOnError && !this.isValid)
 +			Prado.Element.focus(this.options.FocusElementID);
 +
 +		this.visible = true;
 +	},
 +
 +	/**
 +	 * Add a css class to the input control if validator is invalid,
 +	 * removes the css class if valid.
 +	 * @param object html control element
 +	 * @param boolean true to remove the css class, false to add.
 +	 */
 +	updateControlCssClass : function(control, valid)
 +	{
 +		var CssClass = this.options.ControlCssClass;
 +		if(typeof(CssClass) == "string" && CssClass.length > 0)
 +		{
 +			if(valid)
 +				control.removeClassName(CssClass);
 +			else
 +				control.addClassName(CssClass);
 +		}
 +	},
 +
 +	/**
 +	 * Hides the validator messages and remove any validation changes.
 +	 */
 +	hide : function()
 +	{
 +		this.isValid = true;
 +		this.updateControl();
 +		this.visible = false;
 +	},
 +
 +	/**
 +	 * Calls evaluateIsValid() function to set the value of isValid property.
 +	 * Triggers onValidate event and onSuccess or onError event.
 +	 * @param HTMLElement element that calls for validation
 +	 * @return boolean true if valid.
 +	 */
 +	validate : function(invoker)
 +	{
 +		if(typeof(this.options.OnValidate) == "function")
 +			this.options.OnValidate(this, invoker);
 +
 +		if(this.enabled)
 +			this.isValid = this.evaluateIsValid();
 +		else
 +			this.isValid = true;
 +
 +		if(this.isValid)
 +		{
 +			if(typeof(this.options.OnSuccess) == "function")
 +			{
 +				this.visible = true;
 +				this.message.style.visibility = "visible";
 +				this.updateControlCssClass(this.control, this.isValid);
 +				this.options.OnSuccess(this, invoker);
 +			}
 +			else
 +				this.updateControl();
 +		}
 +		else
 +		{
 +			if(typeof(this.options.OnError) == "function")
 +			{
 +				this.visible = true;
 +				this.message.style.visibility = "visible";
 +				this.updateControlCssClass(this.control, this.isValid);
 +				this.options.OnError(this, invoker);
 +			}
 +			else
 +				this.updateControl();
 +		}
 +
 +		this.observeChanges(this.control);
 +
 +		return this.isValid;
 +	},
 +
 +	/**
 +	 * Observe changes to the control input, re-validate upon change. If
 +	 * the validator is not visible, no updates are propagated.
 +	 * @param HTMLElement element that calls for validation
 +	 */
 +	observeChanges : function(control)
 +	{
 +		if(!control) return;
 +
 +		var canObserveChanges = this.options.ObserveChanges != false;
 +		var currentlyObserving = this._isObserving[control.id+this.options.ID];
 +
 +		if(canObserveChanges && !currentlyObserving)
 +		{
 +			var validator = this;
 +
 +			Event.observe(control, 'change', function()
 +			{
 +				if(validator.visible)
 +				{
 +					validator.validate();
 +					validator.manager.updateSummary(validator.group);
 +				}
 +			});
 +			this._isObserving[control.id+this.options.ID] = true;
 +		}
 +	},
 +
 +	/**
 +	 * @return string trims the string value, empty string if value is not string.
 +	 */
 +	trim : function(value)
 +	{
 +		return typeof(value) == "string" ? value.trim() : "";
 +	},
 +
 +	/**
 +	 * Convert the value to a specific data type.
 +	 * @param {string} the data type, "Integer", "Double", "Date" or "String"
 +	 * @param {string} the value to convert.
 +	 * @type {mixed|null} the converted data value.
 +	 */
 +	convert : function(dataType, value)
 +	{
 +		if(typeof(value) == "undefined")
 +			value = this.getValidationValue();
 +		var string = new String(value);
 +		switch(dataType)
 +		{
 +			case "Integer":
 +				return string.toInteger();
 +			case "Double" :
 +			case "Float" :
 +				return string.toDouble(this.options.DecimalChar);
 +			case "Date":
 +				if(typeof(value) != "string")
 +					return value;
 +				else
 +				{
 +					var value = string.toDate(this.options.DateFormat);
 +					if(value && typeof(value.getTime) == "function")
 +						return value.getTime();
 +					else
 +						return null;
 +				}
 +			case "String":
 +				return string.toString();
 +		}
 +		return value;
 +	},
 +
 +	/** 
 +	 * The ControlType property comes from TBaseValidator::getClientControlClass()
 +	 * Be sure to update the TBaseValidator::$_clientClass if new cases are added. 
 +	 * @return mixed control value to validate
 +	 */
 +	 getValidationValue : function(control)
 +	 {
 +	 	if(!control)
 +	 		control = this.control
 +	 	switch(this.options.ControlType)
 +	 	{
 +	 		case 'TDatePicker':
 +	 			if(control.type == "text")
 +	 			{
 +	 				value = this.trim($F(control));
 +	 				
 +	 				if(this.options.DateFormat)
 +	 				{
 +	 					date = value.toDate(this.options.DateFormat);
 +	 					return date == null ? '' : date;
 +	 				}
 +	 				else
 +		 				return value;
 +	 			}
 +	 			else
 +	 			{
 +	 				this.observeDatePickerChanges();
 +
 +	 				return Prado.WebUI.TDatePicker.getDropDownDate(control).getTime();
 +	 			}
 +	 		case 'THtmlArea':
 +	 			if(typeof tinyMCE != "undefined")
 +					tinyMCE.triggerSave();
 +				return this.trim($F(control));
 +			case 'TRadioButton':
 +				if(this.options.GroupName)
 +					return this.getRadioButtonGroupValue();
 +	 		default:
 +	 			if(this.isListControlType())
 +	 				return this.getFirstSelectedListValue();
 +	 			else
 +		 			return this.trim($F(control));
 +	 	}
 +	 },
 +
 +	getRadioButtonGroupValue : function()
 +	{
 +		name = this.control.name;
 +		value = "";
 +		$A(document.getElementsByName(name)).each(function(el)
 +		{
 +			if(el.checked)
 +				value =  el.value;
 +		});
 +		return value;
 +	},
 +
 +	 /**
 +	  * Observe changes in the drop down list date picker, IE only.
 +	  */
 +	 observeDatePickerChanges : function()
 +	 {
 +	 	if(Prado.Browser().ie)
 +	 	{
 +	 		var DatePicker = Prado.WebUI.TDatePicker;
 +	 		this.observeChanges(DatePicker.getDayListControl(this.control));
 +			this.observeChanges(DatePicker.getMonthListControl(this.control));
 +			this.observeChanges(DatePicker.getYearListControl(this.control));
 +	 	}
 +	 },
 +
 +	/**
 +	 * Gets numeber selections and their values.
 +	 * @return object returns selected values in <tt>values</tt> property
 +	 * and number of selections in <tt>checks</tt> property.
 +	 */
 +	getSelectedValuesAndChecks : function(elements, initialValue)
 +	{
 +		var checked = 0;
 +		var values = [];
 +		var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected';
 +		elements.each(function(element)
 +		{
 +			if(element[isSelected] && element.value != initialValue)
 +			{
 +				checked++;
 +				values.push(element.value);
 +			}
 +		});
 +		return {'checks' : checked, 'values' : values};
 +	},
 +
 +	/**
 +	 * Gets an array of the list control item input elements, for TCheckBoxList
 +	 * checkbox inputs are returned, for TListBox HTML option elements are returned.
 +	 * @return array list control option elements.
 +	 */
 +	getListElements : function()
 +	{
 +		switch(this.options.ControlType)
 +		{
 +			case 'TCheckBoxList': case 'TRadioButtonList':
 +				var elements = [];
 +				for(var i = 0; i < this.options.TotalItems; i++)
 +				{
 +					var element = $(this.options.ControlToValidate+"_c"+i);
 +					if(this.isCheckBoxType(element))
 +						elements.push(element);
 +				}
 +				return elements;
 +			case 'TListBox':
 +				var elements = [];
 +				var element = $(this.options.ControlToValidate);
 +				if(element && (type = element.type.toLowerCase()))
 +				{
 +					if(type == "select-one" || type == "select-multiple")
 +						elements = $A(element.options);
 +				}
 +				return elements;
 +			default:
 +				return [];
 +		}
 +	},
 +
 +	/**
 +	 * @return boolean true if element is of checkbox or radio type.
 +	 */
 +	isCheckBoxType : function(element)
 +	{
 +		if(element && element.type)
 +		{
 +			var type = element.type.toLowerCase();
 +			return type == "checkbox" || type == "radio";
 +		}
 +		return false;
 +	},
 +
 +	/**
 +	 * @return boolean true if control to validate is of some of the TListControl type.
 +	 */
 +	isListControlType : function()
 +	{
 +		var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox'];
 +		return list.include(this.options.ControlType);
 +	},
 +
 +	/**
 +	 * @return string gets the first selected list value, initial value if none found.
 +	 */
 +	getFirstSelectedListValue : function()
 +	{
 +		var initial = "";
 +		if(typeof(this.options.InitialValue) != "undefined")
 +			initial = this.options.InitialValue;
 +		var elements = this.getListElements();
 +		var selection = this.getSelectedValuesAndChecks(elements, initial);
 +		return selection.values.length > 0 ? selection.values[0] : initial;
 +	}
 +}
 +
 +
 +/**
 + * TRequiredFieldValidator makes the associated input control a required field.
 + * The input control fails validation if its value does not change from
 + * the <tt>InitialValue<tt> option upon losing focus.
 + * <code>
 + * options['InitialValue']		Validation fails if control input equals initial value.
 + * </code>
 + */
 +Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	/**
 +	 * @return boolean true if the input value is not empty nor equal to the initial value.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var inputType = this.control.getAttribute("type");
 +    	if(inputType == 'file')
 +    	{
 +        	return true;
 +    	}
 +	    else
 +	    {
 +        	var a = this.getValidationValue();
 +        	var b = this.trim(this.options.InitialValue);
 +        	return(a != b);
 +    	}
 +	}
 +});
 +
 +
 +/**
 + * TCompareValidator compares the value entered by the user into an input
 + * control with the value entered into another input control or a constant value.
 + * To compare the associated input control with another input control,
 + * set the <tt>ControlToCompare</tt> option to the ID path
 + * of the control to compare with. To compare the associated input control with
 + * a constant value, specify the constant value to compare with by setting the
 + * <tt>ValueToCompare</tt> option.
 + *
 + * The <tt>DataType</tt> property is used to specify the data type
 + * of both comparison values. Both values are automatically converted to this data
 + * type before the comparison operation is performed. The following value types are supported:
 + * - <b>Integer</b> A 32-bit signed integer data type.
 + * - <b>Float</b> A double-precision floating point number data type.
 + * - <b>Date</b> A date data type. The format can be by the <tt>DateFormat</tt> option.
 + * - <b>String</b> A string data type.
 + *
 + * Use the <tt>Operator</tt> property to specify the type of comparison
 + * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual,
 + * LessThan and LessThanEqual.
 + * <code>
 + * options['ControlToCompare']
 + * options['ValueToCompare']
 + * options['Operator']
 + * options['Type']
 + * options['DateFormat']
 + * </code>
 + */
 +Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	//_observingComparee : false,
 +
 +	/**
 +	 * Compares the input to another input or a given value.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var value = this.getValidationValue();
 +	    if (value.length <= 0)
 +	    	return true;
 +
 +    	var comparee = $(this.options.ControlToCompare);
 +
 +		if(comparee)
 +			var compareTo = this.getValidationValue(comparee);
 +		else
 +			var compareTo = this.options.ValueToCompare || "";
 +
 +	    var isValid =  this.compare(value, compareTo);
 +
 +		if(comparee)
 +		{
 +			this.updateControlCssClass(comparee, isValid);
 +			this.observeChanges(comparee);
 +		}
 +		return isValid;
 +	},
 +
 +	/**
 +	 * Compares two values, their values are casted to type defined
 +	 * by <tt>DataType</tt> option. False is returned if the first
 +	 * operand converts to null. Returns true if the second operand
 +	 * converts to null. The comparision is done based on the
 +	 * <tt>Operator</tt> option.
 +	 */
 +	compare : function(operand1, operand2)
 +	{
 +		var op1, op2;
 +		if((op1 = this.convert(this.options.DataType, operand1)) == null)
 +			return false;
 +		if ((op2 = this.convert(this.options.DataType, operand2)) == null)
 +        	return true;
 +    	switch (this.options.Operator)
 +		{
 +	        case "NotEqual":
 +	            return (op1 != op2);
 +	        case "GreaterThan":
 +	            return (op1 > op2);
 +	        case "GreaterThanEqual":
 +	            return (op1 >= op2);
 +	        case "LessThan":
 +	            return (op1 < op2);
 +	        case "LessThanEqual":
 +	            return (op1 <= op2);
 +	        default:
 +	            return (op1 == op2);
 +	    }
 +	}
 +});
 +
 +/**
 + * TCustomValidator performs user-defined client-side validation on an
 + * input component.
 + *
 + * To create a client-side validation function, add the client-side
 + * validation javascript function to the page template.
 + * The function should have the following signature:
 + * <code>
 + * <script type="text/javascript"><!--
 + * function ValidationFunctionName(sender, parameter)
 + * {
 + *    // if(parameter == ...)
 + *    //    return true;
 + *    // else
 + *    //    return false;
 + * }
 + * -->
 + * </script>
 + * </code>
 + * Use the <tt>ClientValidationFunction</tt> option
 + * to specify the name of the client-side validation script function associated
 + * with the TCustomValidator.
 + * <code>
 + * options['ClientValidationFunction']	custom validation function.
 + * </code>
 + */
 +Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	/**
 +	 * Calls custom validation function.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var value = this.getValidationValue();
 +		var clientFunction = this.options.ClientValidationFunction;
 +		if(typeof(clientFunction) == "string" && clientFunction.length > 0)
 +		{
 +			validate = clientFunction.toFunction();
 +			return validate(this, value);
 +		}
 +		return true;
 +	}
 +});
 +
 +/**
 + * TRangeValidator tests whether an input value is within a specified range.
 + *
 + * TRangeValidator uses three key properties to perform its validation.
 + * The <tt>MinValue</tt> and <tt>MaxValue</tt> options specify the minimum
 + * and maximum values of the valid range. The <tt>DataType</tt> option is
 + * used to specify the data type of the value and the minimum and maximum range values.
 + * These values are converted to this data type before the validation
 + * operation is performed. The following value types are supported:
 + * - <b>Integer</b> A 32-bit signed integer data type.
 + * - <b>Float</b> A double-precision floating point number data type.
 + * - <b>Date</b> A date data type. The date format can be specified by
 + *   setting <tt>DateFormat</tt> option, which must be recognizable
 + *   by <tt>Date.SimpleParse</tt> javascript function.
 + * - <b>String</b> A string data type.
 + * <code>
 + * options['MinValue']		Minimum range value
 + * options['MaxValue']		Maximum range value
 + * options['DataType']		Value data type
 + * options['DateFormat']	Date format for date data type.
 + * </code>
 + */
 +Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	/**
 +	 * Compares the input value with a minimum and/or maximum value.
 +	 * @return boolean true if the value is empty, returns false if conversion fails.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var value = this.getValidationValue();
 +		if(value.length <= 0)
 +			return true;
 +		if(typeof(this.options.DataType) == "undefined")
 +			this.options.DataType = "String";
 +		
 +		if(this.options.DataType != "StringLength")
 +		{
 +			var min = this.convert(this.options.DataType, this.options.MinValue || null);
 +			var max = this.convert(this.options.DataType, this.options.MaxValue || null);
 +			value = this.convert(this.options.DataType, value);
 +		}
 +		else
 +		{
 +			var min = this.options.MinValue || 0;
 +			var max = this.options.MaxValue || Number.POSITIVE_INFINITY;
 +			value = value.length;
 +		}
 +		
 +		if(value == null)
 +			return false;
 +
 +		var valid = true;
 +
 +		if(min != null)
 +			valid = valid && value >= min;
 +		if(max != null)
 +			valid = valid && value <= max;
 +		return valid;
 +	}
 +});
 +
 +/**
 + * TRegularExpressionValidator validates whether the value of an associated
 + * input component matches the pattern specified by a regular expression.
 + * <code>
 + * options['ValidationExpression']	regular expression to match against.
 + * </code>
 + */
 +Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	/**
 +	 * Compare the control input against a regular expression.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var value = this.getValidationValue();
 +	    if (value.length <= 0)
 +	    	return true;
 +
 +	    var rx = new RegExp(this.options.ValidationExpression);
 +	    var matches = rx.exec(value);
 +	    return (matches != null && value == matches[0]);
 +	}
 +});
 +
 +/**
 + * TEmailAddressValidator validates whether the value of an associated
 + * input component is a valid email address.
 + */
 +Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator;
 +
 +
 +/**
 + * TListControlValidator checks the number of selection and their values
 + * for a TListControl that allows multiple selections.
 + */
 +Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	/**
 +	 * @return true if the number of selections and/or their values
 +	 * match the requirements.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		var elements = this.getListElements();
 +		if(elements && elements.length <= 0)
 +			return true;
 +
 +		this.observeListElements(elements);
 +
 +		var selection = this.getSelectedValuesAndChecks(elements);
 +		return this.isValidList(selection.checks, selection.values);
 +	},
 +
 +	/**
 +	 * Observe list elements for IE browsers of changes
 +	 */
 +	 observeListElements : function(elements)
 +	 {
 +		if(Prado.Browser().ie && this.isCheckBoxType(elements[0]))
 +		{
 +			var validator = this;
 +			elements.each(function(element)
 +			{
 +				validator.observeChanges(element);
 +			});
 +		}
 +	 },
 +
 +	/**
 +	 * Determine if the number of checked and the checked values
 +	 * satisfy the required number of checks and/or the checked values
 +	 * equal to the required values.
 +	 * @return boolean true if checked values and number of checks are satisfied.
 +	 */
 +	isValidList : function(checked, values)
 +	{
 +		var exists = true;
 +
 +		//check the required values
 +		var required = this.getRequiredValues();
 +		if(required.length > 0)
 +		{
 +			if(values.length < required.length)
 +				return false;
 +			required.each(function(requiredValue)
 +			{
 +				exists = exists && values.include(requiredValue);
 +			});
 +		}
 +
 +		var min = typeof(this.options.Min) == "undefined" ?
 +					Number.NEGATIVE_INFINITY : this.options.Min;
 +		var max = typeof(this.options.Max) == "undefined" ?
 +					Number.POSITIVE_INFINITY : this.options.Max;
 +		return exists && checked >= min && checked <= max;
 +	},
 +
 +	/**
 +	 * @return array list of required options that must be selected.
 +	 */
 +	getRequiredValues : function()
 +	{
 +		var required = [];
 +		if(this.options.Required && this.options.Required.length > 0)
 +			required = this.options.Required.split(/,\s*/);
 +		return required;
 +	}
 +});
 +
 +
 +/**
 + * TDataTypeValidator verifies if the input data is of the type specified
 + * by <tt>DataType</tt> option.
 + * The following data types are supported:
 + * - <b>Integer</b> A 32-bit signed integer data type.
 + * - <b>Float</b> A double-precision floating point number data type.
 + * - <b>Date</b> A date data type.
 + * - <b>String</b> A string data type.
 + * For <b>Date</b> type, the option <tt>DateFormat</tt>
 + * will be used to determine how to parse the date string.
 + */
 +Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +        evaluateIsValid : function()
 +        {
 +                var value = this.getValidationValue();
 +                if(value.length <= 0)
 +                        return true;
 +                return this.convert(this.options.DataType, value) != null;
 +        }
 +});
 +
 + + | 
