Calendar=function(_1,_2,_3,_4){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.getDateToolTip=null;this.getDateText=null;this.timeout=null;this.onSelected=_3||null;this.onClose=_4||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.firstDayOfWeek=typeof _1=="number"?_1:Calendar._FD;this.showsOtherMonths=false;this.dateStr=_2;this.ar_days=null;this.showsTime=false;this.time24=true;this.yearStep=2;this.hiliteToday=true;this.multiple=null;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined"){Calendar._SDN_len=3;}var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined"){Calendar._SMN_len=3;}ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_ie5=(Calendar.is_ie&&/msie 5\.0/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var _a=/^div$/i.test(el.tagName);if(_a&&el.scrollLeft){SL=el.scrollLeft;}if(_a&&el.scrollTop){ST=el.scrollTop;}var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var _c=this.getAbsolutePos(el.offsetParent);r.x+=_c.x;r.y+=_c.y;}return r;};Calendar.isRelated=function(el,_e){var _f=_e.relatedTarget;if(!_f){var _10=_e.type;if(_10=="mouseover"){_f=_e.fromElement;}else{if(_10=="mouseout"){_f=_e.toElement;}}}while(_f){if(_f==el){return true;}_f=_f.parentNode;}return false;};Calendar.removeClass=function(el,_12){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=_12){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,_17){Calendar.removeClass(el,_17);el.className+=" "+_17;};Calendar.getElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.currentTarget;while(f.nodeType!=1||/^div$/i.test(f.tagName)){f=f.parentNode;}return f;};Calendar.getTargetElement=function(ev){var f=Calendar.is_ie?window.event.srcElement:ev.target;while(f.nodeType!=1){f=f.parentNode;}return f;};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,_1e,_1f){if(el.attachEvent){el.attachEvent("on"+_1e,_1f);}else{if(el.addEventListener){el.addEventListener(_1e,_1f,true);}else{el["on"+_1e]=_1f;}}};Calendar.removeEvent=function(el,_21,_22){if(el.detachEvent){el.detachEvent("on"+_21,_22);}else{if(el.removeEventListener){el.removeEventListener(_21,_22,true);}else{el["on"+_21]=null;}}};Calendar.createElement=function(_23,_24){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",_23);}else{el=document.createElement(_23);}if(typeof _24!="undefined"){_24.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else{if(typeof el.parentNode.month!="undefined"){return el.parentNode;}}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else{if(typeof el.parentNode.year!="undefined"){return el.parentNode;}}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0){s.left=cd.offsetLeft+"px";}else{var mcw=mc.offsetWidth;if(typeof mcw=="undefined"){mcw=50;}s.left=(cd.offsetLeft+cd.offsetWidth-mcw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var _37=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.innerHTML=Y;yr.year=Y;yr.style.display="block";_37=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?cal.yearStep:-cal.yearStep;}if(_37){var s=yc.style;s.display="block";if(cd.navtype<0){s.left=cd.offsetLeft+"px";}else{var ycw=yc.offsetWidth;if(typeof ycw=="undefined"){ycw=50;}s.left=(cd.offsetLeft+cd.offsetWidth-ycw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var _3e=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(_3e==el||_3e.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(_3e);var _40=null;if(mon){_40=new Date(cal.date);if(mon.month!=_40.getMonth()){_40.setMonth(mon.month);cal.setDate(_40);cal.dateClicked=false;cal.callHandler();}}else{var _41=Calendar.findYear(_3e);if(_41){_40=new Date(cal.date);if(_41.year!=_40.getFullYear()){_40.setFullYear(_41.year);cal.setDate(_40);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var _45=Calendar.getTargetElement(ev);if(_45==el||_45.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2))){Calendar.removeClass(el,"active");}Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&_45!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var _4a=true;if(x>pos.x+w){dx=x-pos.x-w;_4a=false;}else{dx=pos.x-x;}if(dx<0){dx=0;}var _4b=el._range;var _4c=el._current;var _4d=Math.floor(dx/10)%_4b.length;for(var i=_4b.length;--i>=0;){if(_4b[i]==_4c){break;}}while(_4d-->0){if(_4a){if(--i<0){i=_4b.length-1;}}else{if(++i>=_4b.length){i=0;}}}var _4f=_4b[i];el.innerHTML=_4f;cal.onUpdateTime();}var mon=Calendar.findMonth(_45);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var _51=Calendar.findYear(_45);if(_51){if(_51.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(_51,"hilite");cal.hilitedYear=_51;}else{if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}}else{if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var _55;var _56;if(Calendar.is_ie){_56=window.event.clientY+document.body.scrollTop;_55=window.event.clientX+document.body.scrollLeft;}else{_55=ev.pageX;_56=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(_55-cal.xOffs)+"px";st.top=(_56-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300){with(Calendar){if(el.navtype==50){el._current=el.innerHTML;addEvent(document,"mousemove",tableMouseOver);}else{addEvent(document,Calendar.is_ie5?"mousemove":"mouseover",tableMouseOver);}addClass(el,"hilite active");addEvent(document,"mouseup",tableMouseUp);}}else{if(cal.isPopup){cal._dragStart(ev);}}if(el.navtype==-1||el.navtype==1){if(cal.timeout){clearTimeout(cal.timeout);}cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else{if(el.navtype==-2||el.navtype==2){if(cal.timeout){clearTimeout(cal.timeout);}cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){el.ttip=el.caldate.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.innerHTML=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled){return false;}removeClass(el,"hilite");if(el.caldate){removeClass(el.parentNode,"rowhilite");}if(el.calendar){el.calendar.tooltips.innerHTML=_TT["SEL_DATE"];}return stopEvent(ev);}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var _65=false;var _66=false;var _67=null;if(typeof el.navtype=="undefined"){if(cal.currentDateEl){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");_65=(cal.currentDateEl==el);if(!_65){cal.currentDateEl=el;}}cal.date.setDateOnly(el.caldate);_67=cal.date;var _68=!(cal.dateClicked=!el.otherMonth);if(!_68&&!cal.currentDateEl){cal._toggleMultipleDate(new Date(_67));}else{_66=!el.disabled;}if(_68){cal._init(cal.firstDayOfWeek,_67);}}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}_67=new Date(cal.date);if(el.navtype==0){_67.setDateOnly(new Date());}cal.dateClicked=false;var _69=_67.getFullYear();var mon=_67.getMonth();function setMonth(m){var day=_67.getDate();var max=_67.getMonthDays(m);if(day>max){_67.setDate(max);}_67.setMonth(m);}switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var _6e=Calendar._TT["ABOUT"];if(typeof _6e!="undefined"){_6e+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{_6e="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(_6e);return;case -2:if(_69>cal.minYear){_67.setFullYear(_69-1);}break;case -1:if(mon>0){setMonth(mon-1);}else{if(_69-->cal.minYear){_67.setFullYear(_69);setMonth(11);}}break;case 1:if(mon<11){setMonth(mon+1);}else{if(_69<cal.maxYear){_67.setFullYear(_69+1);setMonth(0);}}break;case 2:if(_69<cal.maxYear){_67.setFullYear(_69+1);}break;case 100:cal.setFirstDayOfWeek(el.fdow);return;case 50:var _6f=el._range;var _70=el.innerHTML;for(var i=_6f.length;--i>=0;){if(_6f[i]==_70){break;}}if(ev&&ev.shiftKey){if(--i<0){i=_6f.length-1;}}else{if(++i>=_6f.length){i=0;}}var _72=_6f[i];el.innerHTML=_72;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(_67,_67.getFullYear(),_67.getMonth(),_67.getDate())){return false;}break;}if(!_67.equalsTo(cal.date)){cal.setDate(_67);_66=true;}else{if(el.navtype==0){_66=_65=true;}}}if(_66){ev&&cal.callHandler();}if(_65){Calendar.removeClass(el,"hilite");ev&&cal.callCloseHandler();}};Calendar.prototype.create=function(_73){var _74=null;if(!_73){_74=document.getElementsByTagName("body")[0];this.isPopup=true;}else{_74=_73;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var _75=Calendar.createElement("table");this.table=_75;_75.cellSpacing=0;_75.cellPadding=0;_75.calendar=this;Calendar.addEvent(_75,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(_75);var _77=Calendar.createElement("thead",_75);var _78=null;var row=null;var cal=this;var hh=function(_7c,cs,_7e){_78=Calendar.createElement("td",row);_78.colSpan=cs;_78.className="jscalendar-button";if(_7e!=0&&Math.abs(_7e)<=2){_78.className+=" nav";}Calendar._add_evs(_78);_78.calendar=cal;_78.navtype=_7e;_78.innerHTML="<div unselectable='on'>"+_7c+"</div>";return _78;};row=Calendar.createElement("tr",_77);var _7f=6;(this.isPopup)&&--_7f;(this.weekNumbers)&&++_7f;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",_7f,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("&#x00d7;",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",_77);row.className="headrow";this._nav_py=hh("&#x00ab;",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("&#x2039;",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("&#x203a;",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("&#x00bb;",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",_77);row.className="daynames";if(this.weekNumbers){_78=Calendar.createElement("td",row);_78.className="name wn";_78.innerHTML=Calendar._TT["WK"];}for(var i=7;i>0;--i){_78=Calendar.createElement("td",row);if(!i){_78.navtype=100;_78.calendar=this;Calendar._add_evs(_78);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var _81=Calendar.createElement("tbody",_75);this.tbody=_81;for(i=6;i>0;--i){row=Calendar.createElement("tr",_81);if(this.weekNumbers){_78=Calendar.createElement("td",row);}for(var j=7;j>0;--j){_78=Calendar.createElement("td",row);_78.calendar=this;Calendar._add_evs(_78);}}if(this.showsTime){row=Calendar.createElement("tr",_81);row.className="time";_78=Calendar.createElement("td",row);_78.className="time";_78.colSpan=2;_78.innerHTML=Calendar._TT["TIME"]||"&#160;";_78=Calendar.createElement("td",row);_78.className="time";_78.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(_83,_84,_85,_86){var _87=Calendar.createElement("span",_78);_87.className=_83;_87.innerHTML=_84;_87.calendar=cal;_87.ttip=Calendar._TT["TIME_PART"];_87.navtype=50;_87._range=[];if(typeof _85!="number"){_87._range=_85;}else{for(var i=_85;i<=_86;++i){var txt;if(i<10&&_86>=10){txt="0"+i;}else{txt=""+i;}_87._range[_87._range.length]=txt;}}Calendar._add_evs(_87);return _87;}var hrs=cal.date.getHours();var _8b=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm){hrs-=12;}var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var _8f=Calendar.createElement("span",_78);_8f.innerHTML=":";_8f.className="colon";var M=makeTimePart("minute",_8b,0,59);var AP=null;_78=Calendar.createElement("td",row);_78.className="time";_78.colSpan=2;if(t12){AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);}else{_78.innerHTML="&#160;";}cal.onSetTime=function(){var pm,hrs=this.date.getHours(),_8b=this.date.getMinutes();if(t12){pm=(hrs>=12);if(pm){hrs-=12;}if(hrs==0){hrs=12;}AP.innerHTML=pm?"pm":"am";}H.innerHTML=(hrs<10)?("0"+hrs):hrs;M.innerHTML=(_8b<10)?("0"+_8b):_8b;};cal.onUpdateTime=function(){var _93=this.date;var h=parseInt(H.innerHTML,10);if(t12){if(/pm/i.test(AP.innerHTML)&&h<12){h+=12;}else{if(/am/i.test(AP.innerHTML)&&h==12){h=0;}}}var d=_93.getDate();var m=_93.getMonth();var y=_93.getFullYear();_93.setHours(h);_93.setMinutes(parseInt(M.innerHTML,10));_93.setFullYear(y);_93.setMonth(m);_93.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var _98=Calendar.createElement("tfoot",_75);row=Calendar.createElement("tr",_98);row.className="footrow";_78=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);_78.className="ttip";if(this.isPopup){_78.ttip=Calendar._TT["DRAG_TO_MOVE"];_78.style.cursor="move";}this.tooltips=_78;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.innerHTML=Calendar._SMN[i];div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";div.appendChild(yr);}this._init(this.firstDayOfWeek,this.date);_74.appendChild(this.element);};Calendar._keyEvent=function(ev){var cal=window._dynarch_popupCalendar;if(!cal||cal.multiple){return false;}(Calendar.is_ie)&&(ev=window.event);var act=(Calendar.is_ie||ev.type=="keypress"),K=ev.keyCode;if(ev.ctrlKey){switch(K){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else{switch(K){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.callCloseHandler();break;case 37:case 38:case 39:case 40:if(act){var _9e,x,y,ne,el,step;_9e=K==37||K==38;step=(K==37||K==39)?1:7;function setVars(){el=cal.currentDateEl;var p=el.pos;x=p&15;y=p>>4;ne=cal.ar_days[y][x];}setVars();function prevMonth(){var _a0=new Date(cal.date);_a0.setDate(_a0.getDate()-step);cal.setDate(_a0);}function nextMonth(){var _a1=new Date(cal.date);_a1.setDate(_a1.getDate()+step);cal.setDate(_a1);}while(1){switch(K){case 37:if(--x>=0){ne=cal.ar_days[y][x];}else{x=6;K=38;continue;}break;case 38:if(--y>=0){ne=cal.ar_days[y][x];}else{prevMonth();setVars();}break;case 39:if(++x<7){ne=cal.ar_days[y][x];}else{x=0;K=40;continue;}break;case 40:if(++y<cal.ar_days.length){ne=cal.ar_days[y][x];}else{nextMonth();setVars();}break;}break;}if(ne){if(!ne.disabled){Calendar.cellClick(ne);}else{if(_9e){prevMonth();}else{nextMonth();}}}}break;case 13:if(act){Calendar.cellClick(cal.currentDateEl,ev);}break;default:return false;}}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(_a2,_a3){var _a4=new Date(),TY=_a4.getFullYear(),TM=_a4.getMonth(),TD=_a4.getDate();this.table.style.visibility="hidden";var _a5=_a3.getFullYear();if(_a5<this.minYear){_a5=this.minYear;_a3.setFullYear(_a5);}else{if(_a5>this.maxYear){_a5=this.maxYear;_a3.setFullYear(_a5);}}this.firstDayOfWeek=_a2;this.date=new Date(_a3);var _a6=_a3.getMonth();var _a7=_a3.getDate();var _a8=_a3.getMonthDays();_a3.setDate(1);var _a9=(_a3.getDay()-this.firstDayOfWeek)%7;if(_a9<0){_a9+=7;}_a3.setDate(-_a9);_a3.setDate(_a3.getDate()+1);var row=this.tbody.firstChild;var MN=Calendar._SMN[_a6];var _ac=this.ar_days=new Array();var _ad=Calendar._TT["WEEKEND"];var _ae=this.multiple?(this.datesCells={}):null;for(var i=0;i<6;++i,row=row.nextSibling){var _b0=row.firstChild;if(this.weekNumbers){_b0.className="day wn";_b0.innerHTML=_a3.getWeekNumber();_b0=_b0.nextSibling;}row.className="daysrow";var _b1=false,iday,dpos=_ac[i]=[];for(var j=0;j<7;++j,_b0=_b0.nextSibling,_a3.setDate(iday+1)){iday=_a3.getDate();var _b3=_a3.getDay();_b0.className="day";_b0.pos=i<<4|j;dpos[j]=_b0;var _b4=(_a3.getMonth()==_a6);if(!_b4){if(this.showsOtherMonths){_b0.className+=" othermonth";_b0.otherMonth=true;}else{_b0.className="emptycell";_b0.innerHTML="&#160;";_b0.disabled=true;continue;}}else{_b0.otherMonth=false;_b1=true;}_b0.disabled=false;_b0.innerHTML=this.getDateText?this.getDateText(_a3,iday):iday;if(_ae){_ae[_a3.print("%Y%m%d")]=_b0;}if(this.getDateStatus){var _b5=this.getDateStatus(_a3,_a5,_a6,iday);if(this.getDateToolTip){var _b6=this.getDateToolTip(_a3,_a5,_a6,iday);if(_b6){_b0.title=_b6;}}if(_b5===true){_b0.className+=" disabled";_b0.disabled=true;}else{if(/disabled/i.test(_b5)){_b0.disabled=true;}_b0.className+=" "+_b5;}}if(!_b0.disabled){_b0.caldate=new Date(_a3);_b0.ttip="_";if(!this.multiple&&_b4&&iday==_a7&&this.hiliteToday){_b0.className+=" selected";this.currentDateEl=_b0;}if(_a3.getFullYear()==TY&&_a3.getMonth()==TM&&iday==TD){_b0.className+=" today";_b0.ttip+=Calendar._TT["PART_TODAY"];}if(_ad.indexOf(_b3.toString())!=-1){_b0.className+=_b0.otherMonth?" oweekend":" weekend";}}}if(!(_b1||this.showsOtherMonths)){row.className="emptyrow";}}this.title.innerHTML=Calendar._MN[_a6]+", "+_a5;this.onSetTime();this.table.style.visibility="visible";this._initMultipleDates();};Calendar.prototype._initMultipleDates=function(){if(this.multiple){for(var i in this.multiple){var _b8=this.datesCells[i];var d=this.multiple[i];if(!d){continue;}if(_b8){_b8.className+=" selected";}}}};Calendar.prototype._toggleMultipleDate=function(_ba){if(this.multiple){var ds=_ba.print("%Y%m%d");var _bc=this.datesCells[ds];if(_bc){var d=this.multiple[ds];if(!d){Calendar.addClass(_bc,"selected");this.multiple[ds]=_ba;}else{Calendar.removeClass(_bc,"selected");delete this.multiple[ds];}}}};Calendar.prototype.setDateToolTipHandler=function(_be){this.getDateToolTip=_be;};Calendar.prototype.setDate=function(_bf){if(!_bf.equalsTo(this.date)){this._init(this.firstDayOfWeek,_bf);}};Calendar.prototype.refresh=function(){this._init(this.firstDayOfWeek,this.date);};Calendar.prototype.setFirstDayOfWeek=function(_c0){this._init(_c0,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(_c1){this.getDateStatus=_c1;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window._dynarch_popupCalendar=null;};Calendar.prototype.reparent=function(_c5){var el=this.element;el.parentNode.removeChild(el);_c5.appendChild(el);};Calendar._checkCalendar=function(ev){var _c8=window._dynarch_popupCalendar;if(!_c8){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=_c8.element;el=el.parentNode){}if(el==null){window._dynarch_popupCalendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var _ca=this.table.getElementsByTagName("tr");for(var i=_ca.length;i>0;){var row=_ca[--i];Calendar.removeClass(row,"rowhilite");var _cd=row.getElementsByTagName("td");for(var j=_cd.length;j>0;){var _cf=_cd[--j];Calendar.removeClass(_cf,"hilite");Calendar.removeClass(_cf,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window._dynarch_popupCalendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,_d4){var _d5=this;var p=Calendar.getAbsolutePos(el);if(!_d4||typeof _d4!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}function fixPosition(box){if(box.x<0){box.x=0;}if(box.y<0){box.y=0;}var cp=document.createElement("div");var s=cp.style;s.position="absolute";s.right=s.bottom=s.width=s.height="0px";document.body.appendChild(cp);var br=Calendar.getAbsolutePos(cp);document.body.removeChild(cp);if(Calendar.is_ie){br.y+=document.body.scrollTop;br.x+=document.body.scrollLeft;}else{br.y+=window.scrollY;br.x+=window.scrollX;}var tmp=box.x+box.width-br.x;if(tmp>0){box.x-=tmp;}tmp=box.y+box.height-br.y;if(tmp>0){box.y-=tmp;}}this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=_d5.element.offsetWidth;var h=_d5.element.offsetHeight;_d5.element.style.display="none";var _de=_d4.substr(0,1);var _df="l";if(_d4.length>1){_df=_d4.substr(1,1);}switch(_de){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(_df){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "l":p.x+=el.offsetWidth-w;break;case "r":break;}p.width=w;p.height=h+40;_d5.monthsCombo.style.display="none";fixPosition(p);_d5.showAt(p.x,p.y);};if(Calendar.is_khtml){setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);}else{Calendar.continuation_for_the_fucking_khtml_browser();}};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){if(!fmt){fmt=this.dateFormat;}this.setDate(Date.parseDate(str,fmt));};Calendar.prototype.hideShowCovered=function(){if(!Calendar.is_ie&&!Calendar.is_opera){return;}function getVisib(obj){var _e5=obj.style.visibility;if(!_e5){if(document.defaultView&&typeof (document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml){_e5=document.defaultView.getComputedStyle(obj,"").getPropertyValue("visibility");}else{_e5="";}}else{if(obj.currentStyle){_e5=obj.currentStyle.visibility;}else{_e5="";}}}return _e5;}var _e6=new Array("applet","iframe","select");var el=this.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=_e6.length;k>0;){var ar=document.getElementsByTagName(_e6[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(this.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};Calendar.prototype._displayWeekdays=function(){var _f5=this.firstDayOfWeek;var _f6=this.firstdayname;var _f7=Calendar._TT["WEEKEND"];for(var i=0;i<7;++i){_f6.className="day name";var _f9=(i+_f5)%7;if(i){_f6.ttip=Calendar._TT["DAY_FIRST"].replace("%s",Calendar._DN[_f9]);_f6.navtype=100;_f6.calendar=this;_f6.fdow=_f9;Calendar._add_evs(_f6);}if(_f7.indexOf(_f9.toString())!=-1){Calendar.addClass(_f6,"weekend");}_f6.innerHTML=Calendar._SDN[(i+_f5)%7];_f6=_f6.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var _fb;var _fc;if(Calendar.is_ie){_fc=window.event.clientY+document.body.scrollTop;_fb=window.event.clientX+document.body.scrollLeft;}else{_fc=ev.clientY+window.scrollY;_fb=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=_fb-parseInt(st.left);this.yOffs=_fc-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.parseDate=function(str,fmt){var _100=new Date();var y=0;var m=-1;var d=0;var a=str.split(/\W+/);var b=fmt.match(/%./g);var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(!a[i]){continue;}switch(b[i]){case "%d":case "%e":d=parseInt(a[i],10);break;case "%m":m=parseInt(a[i],10)-1;break;case "%Y":case "%y":y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);break;case "%b":case "%B":for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}break;case "%H":case "%I":case "%k":case "%l":hr=parseInt(a[i],10);break;case "%P":case "%p":if(/pm/i.test(a[i])&&hr<12){hr+=12;}else{if(/am/i.test(a[i])&&hr>=12){hr-=12;}}break;case "%M":min=parseInt(a[i],10);break;}}if(isNaN(y)){y=_100.getFullYear();}if(isNaN(m)){m=_100.getMonth();}if(isNaN(d)){d=_100.getDate();}if(isNaN(hr)){hr=_100.getHours();}if(isNaN(min)){min=_100.getMinutes();}if(y!=0&&m!=-1&&d!=0){return new Date(y,m,d,hr,min,0);}y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else{if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else{if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else{if(d==0){d=a[i];}}}}}if(y==0){y=_100.getFullYear();}if(m!=-1&&d!=0){return new Date(y,m,d,hr,min,0);}return _100;};Date.prototype.getMonthDays=function(_10a){var year=this.getFullYear();if(typeof _10a=="undefined"){_10a=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&_10a==1){return 29;}else{return Date._MD[_10a];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,0,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var d=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var DoW=d.getDay();d.setDate(d.getDate()-(DoW+6)%7+3);var ms=d.valueOf();d.setMonth(0);d.setDate(4);return Math.round((ms-d.valueOf())/(7*86400000))+1;};Date.prototype.equalsTo=function(date){return ((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.setDateOnly=function(date){var tmp=new Date(date);this.setDate(1);this.setFullYear(tmp.getFullYear());this.setMonth(tmp.getMonth());this.setDate(tmp.getDate());};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0){ir=12;}var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(""+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=/%./g;if(!Calendar.is_ie5&&!Calendar.is_khtml){return str.replace(re,function(par){return s[par]||par;});}var a=str.match(re);for(var i=0;i<a.length;i++){var tmp=s[a[i]];if(tmp){re=new RegExp(a[i],"g");str=str.replace(re,tmp);}}return str;};if(!Date.prototype.__msh_oldSetFullYear){Date.prototype.__msh_oldSetFullYear=Date.prototype.setFullYear;}Date.prototype.setFullYear=function(y){var d=new Date(this);d.__msh_oldSetFullYear(y);if(d.getMonth()!=this.getMonth()){this.setDate(28);}this.__msh_oldSetFullYear(y);};window._dynarch_popupCalendar=null;
Calendar._DN=new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday");Calendar._SDN=new Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun");Calendar._FD=0;Calendar._MN=new Array("January","February","March","April","May","June","July","August","September","October","November","December");Calendar._SMN=new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");Calendar._TT={};Calendar._TT["INFO"]="About the calendar";Calendar._TT["ABOUT"]="DHTML Date/Time Selector\n"+"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n"+"For latest version visit: http://www.dynarch.com/projects/calendar/\n"+"Distributed under GNU LGPL.  See http://gnu.org/licenses/lgpl.html for details."+"\n\n"+"Date selection:\n"+"- Use the \xab, \xbb buttons to select year\n"+"- Use the "+String.fromCharCode(8249)+", "+String.fromCharCode(8250)+" buttons to select month\n"+"- Hold mouse button on any of the above buttons for faster selection.";Calendar._TT["ABOUT_TIME"]="\n\n"+"Time selection:\n"+"- Click on any of the time parts to increase it\n"+"- or Shift-click to decrease it\n"+"- or click and drag for faster selection.";Calendar._TT["PREV_YEAR"]="Prev. year (hold for menu)";Calendar._TT["PREV_MONTH"]="Prev. month (hold for menu)";Calendar._TT["GO_TODAY"]="Go Today";Calendar._TT["NEXT_MONTH"]="Next month (hold for menu)";Calendar._TT["NEXT_YEAR"]="Next year (hold for menu)";Calendar._TT["SEL_DATE"]="Select date";Calendar._TT["DRAG_TO_MOVE"]="Drag to move";Calendar._TT["PART_TODAY"]=" (today)";Calendar._TT["DAY_FIRST"]="Display %s first";Calendar._TT["WEEKEND"]="0,6";Calendar._TT["CLOSE"]="Close";Calendar._TT["TODAY"]="Today";Calendar._TT["TIME_PART"]="(Shift-)Click or drag to change value";Calendar._TT["DEF_DATE_FORMAT"]="%Y-%m-%d";Calendar._TT["TT_DATE_FORMAT"]="%a, %b %e";Calendar._TT["WK"]="wk";Calendar._TT["TIME"]="Time:";
Calendar.setup=function(_1){function param_default(_2,_3){if(typeof _1[_2]=="undefined"){_1[_2]=_3;}}param_default("inputField",null);param_default("displayArea",null);param_default("button",null);param_default("eventName","click");param_default("ifFormat","%Y/%m/%d");param_default("daFormat","%Y/%m/%d");param_default("singleClick",true);param_default("disableFunc",null);param_default("dateStatusFunc",_1["disableFunc"]);param_default("dateText",null);param_default("firstDay",null);param_default("align","Br");param_default("range",[1900,2999]);param_default("weekNumbers",true);param_default("flat",null);param_default("flatCallback",null);param_default("onSelect",null);param_default("onClose",null);param_default("onUpdate",null);param_default("date",null);param_default("showsTime",false);param_default("timeFormat","24");param_default("electric",true);param_default("step",2);param_default("position",null);param_default("cache",false);param_default("showOthers",false);param_default("multiple",null);var _4=["inputField","displayArea","button"];for(var i in _4){if(typeof _1[_4[i]]=="string"){_1[_4[i]]=document.getElementById(_1[_4[i]]);}}if(!(_1.flat||_1.multiple||_1.inputField||_1.displayArea||_1.button)){alert("Calendar.setup:\n  Nothing to setup (no fields found).  Please check your code");return false;}function onSelect(_6){var p=_6.params;var _8=(_6.dateClicked||p.electric);if(_8&&p.inputField){p.inputField.value=_6.date.print(p.ifFormat);if(typeof p.inputField.onchange=="function"){p.inputField.onchange();}}if(_8&&p.displayArea){p.displayArea.innerHTML=_6.date.print(p.daFormat);}if(_8&&typeof p.onUpdate=="function"){p.onUpdate(_6);}if(_8&&p.flat){if(typeof p.flatCallback=="function"){p.flatCallback(_6);}}if(_8&&p.singleClick&&_6.dateClicked){_6.callCloseHandler();}}if(_1.flat!=null){if(typeof _1.flat=="string"){_1.flat=document.getElementById(_1.flat);}if(!_1.flat){alert("Calendar.setup:\n  Flat specified but can't find parent.");return false;}var _9=new Calendar(_1.firstDay,_1.date,_1.onSelect||onSelect);_9.showsOtherMonths=_1.showOthers;_9.showsTime=_1.showsTime;_9.time24=(_1.timeFormat=="24");_9.params=_1;_9.weekNumbers=_1.weekNumbers;_9.setRange(_1.range[0],_1.range[1]);_9.setDateStatusHandler(_1.dateStatusFunc);_9.getDateText=_1.dateText;if(_1.ifFormat){_9.setDateFormat(_1.ifFormat);}if(_1.inputField&&typeof _1.inputField.value=="string"){_9.parseDate(_1.inputField.value);}_9.create(_1.flat);_9.show();return false;}var _a=_1.button||_1.displayArea||_1.inputField;_a["on"+_1.eventName]=function(){var _b=_1.inputField||_1.displayArea;var _c=_1.inputField?_1.ifFormat:_1.daFormat;var _d=false;var _e=window.calendar;if(_b){_1.date=Date.parseDate(_b.value||_b.innerHTML,_c);}if(!(_e&&_1.cache)){window.calendar=_e=new Calendar(_1.firstDay,_1.date,_1.onSelect||onSelect,_1.onClose||function(_f){_f.hide();});_e.showsTime=_1.showsTime;_e.time24=(_1.timeFormat=="24");_e.weekNumbers=_1.weekNumbers;_d=true;}else{if(_1.date){_e.setDate(_1.date);}_e.hide();}if(_1.multiple){_e.multiple={};for(var i=_1.multiple.length;--i>=0;){var d=_1.multiple[i];var ds=d.print("%Y%m%d");_e.multiple[ds]=d;}}_e.showsOtherMonths=_1.showOthers;_e.yearStep=_1.step;_e.setRange(_1.range[0],_1.range[1]);_e.params=_1;_e.setDateStatusHandler(_1.dateStatusFunc);_e.getDateText=_1.dateText;_e.setDateFormat(_c);if(_d){_e.create();}_e.refresh();if(!_1.position){_e.showAtElement(_1.button||_1.displayArea||_1.inputField,_1.align);}else{_e.showAt(_1.position[0],_1.position[1]);}return false;};return _9;};
/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ if(typeof YAHOO=="undefined"){var YAHOO={};}YAHOO.namespace=function(){var a=arguments,o=null,i,j,d;for(i=0;i<a.length;++i){d=a[i].split(".");o=YAHOO;for(j=(d[0]=="YAHOO")?1:0;j<d.length;++j){o[d[j]]=o[d[j]]||{};o=o[d[j]];}}return o;};YAHOO.log=function(_2,_3,_4){var l=YAHOO.widget.Logger;if(l&&l.log){return l.log(_2,_3,_4);}else{return false;}};YAHOO.extend=function(_6,_7,_8){var F=function(){};F.prototype=_7.prototype;_6.prototype=new F();_6.prototype.constructor=_6;_6.superclass=_7.prototype;if(_7.prototype.constructor==Object.prototype.constructor){_7.prototype.constructor=_7;}if(_8){for(var i in _8){_6.prototype[i]=_8[i];}}};YAHOO.augment=function(r,s){var rp=r.prototype,sp=s.prototype,a=arguments,i,p;if(a[2]){for(i=2;i<a.length;++i){rp[a[i]]=sp[a[i]];}}else{for(p in sp){if(!rp[p]){rp[p]=sp[p];}}}};YAHOO.namespace("util","widget","example");
/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ YAHOO.util.CustomEvent=function(_1,_2,_3,_4){this.type=_1;this.scope=_2||window;this.silent=_3;this.signature=_4||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var _5="_YUICEOnSubscribe";if(_1!==_5){this.subscribeEvent=new YAHOO.util.CustomEvent(_5,this,true);}};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(fn,_7,_8){if(this.subscribeEvent){this.subscribeEvent.fire(fn,_7,_8);}this.subscribers.push(new YAHOO.util.Subscriber(fn,_7,_8));},unsubscribe:function(fn,_9){var _10=false;for(var i=0,len=this.subscribers.length;i<len;++i){var s=this.subscribers[i];if(s&&s.contains(fn,_9)){this._delete(i);_10=true;}}return _10;},fire:function(){var len=this.subscribers.length;if(!len&&this.silent){return true;}var _14=[],ret=true,i;for(i=0;i<arguments.length;++i){_14.push(arguments[i]);}var _15=_14.length;if(!this.silent){}for(i=0;i<len;++i){var s=this.subscribers[i];if(s){if(!this.silent){}var _16=s.getScope(this.scope);if(this.signature==YAHOO.util.CustomEvent.FLAT){var _17=null;if(_14.length>0){_17=_14[0];}ret=s.fn.call(_16,_17,s.obj);}else{ret=s.fn.call(_16,this.type,_14,s.obj);}if(false===ret){if(!this.silent){}return false;}}}return true;},unsubscribeAll:function(){for(var i=0,len=this.subscribers.length;i<len;++i){this._delete(len-1-i);}},_delete:function(_18){var s=this.subscribers[_18];if(s){delete s.fn;delete s.obj;}this.subscribers.splice(_18,1);},toString:function(){return "CustomEvent: "+"'"+this.type+"', "+"scope: "+this.scope;}};YAHOO.util.Subscriber=function(fn,obj,_20){this.fn=fn;this.obj=obj||null;this.override=_20;};YAHOO.util.Subscriber.prototype.getScope=function(_21){if(this.override){if(this.override===true){return this.obj;}else{return this.override;}}return _21;};YAHOO.util.Subscriber.prototype.contains=function(fn,obj){if(obj){return (this.fn==fn&&this.obj==obj);}else{return (this.fn==fn);}};YAHOO.util.Subscriber.prototype.toString=function(){return "Subscriber { obj: "+(this.obj||"")+", override: "+(this.override||"no")+" }";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var _22=false;var _23=[];var _24=[];var _25=[];var _26=[];var _27=0;var _28=[];var _29=[];var _30=0;return {POLL_RETRYS:200,POLL_INTERVAL:20,EL:0,TYPE:1,FN:2,WFN:3,OBJ:3,ADJ_SCOPE:4,isSafari:(/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),isIE:(!this.isSafari&&!navigator.userAgent.match(/opera/gi)&&navigator.userAgent.match(/msie/gi)),_interval:null,startInterval:function(){if(!this._interval){var _31=this;var _32=function(){_31._tryPreloadAttach();};this._interval=setInterval(_32,this.POLL_INTERVAL);}},onAvailable:function(_33,_34,_35,_36){_28.push({id:_33,fn:_34,obj:_35,override:_36,checkReady:false});_27=this.POLL_RETRYS;this.startInterval();},onContentReady:function(_37,_38,_39,_40){_28.push({id:_37,fn:_38,obj:_39,override:_40,checkReady:true});_27=this.POLL_RETRYS;this.startInterval();},addListener:function(el,_42,fn,obj,_43){if(!fn||!fn.call){return false;}if(this._isValidCollection(el)){var ok=true;for(var i=0,len=el.length;i<len;++i){ok=this.on(el[i],_42,fn,obj,_43)&&ok;}return ok;}else{if(typeof el=="string"){var oEl=this.getEl(el);if(oEl){el=oEl;}else{this.onAvailable(el,function(){YAHOO.util.Event.on(el,_42,fn,obj,_43);});return true;}}}if(!el){return false;}if("unload"==_42&&obj!==this){_24[_24.length]=[el,_42,fn,obj,_43];return true;}var _46=el;if(_43){if(_43===true){_46=obj;}else{_46=_43;}}var _47=function(e){return fn.call(_46,YAHOO.util.Event.getEvent(e),obj);};var li=[el,_42,fn,_47,_46];var _50=_23.length;_23[_50]=li;if(this.useLegacyEvent(el,_42)){var _51=this.getLegacyIndex(el,_42);if(_51==-1||el!=_25[_51][0]){_51=_25.length;_29[el.id+_42]=_51;_25[_51]=[el,_42,el["on"+_42]];_26[_51]=[];el["on"+_42]=function(e){YAHOO.util.Event.fireLegacyEvent(YAHOO.util.Event.getEvent(e),_51);};}_26[_51].push(li);}else{this._simpleAdd(el,_42,_47,false);}return true;},fireLegacyEvent:function(e,_52){var ok=true;var le=_26[_52];for(var i=0,len=le.length;i<len;++i){var li=le[i];if(li&&li[this.WFN]){var _54=li[this.ADJ_SCOPE];var ret=li[this.WFN].call(_54,e);ok=(ok&&ret);}}return ok;},getLegacyIndex:function(el,_56){var key=this.generateId(el)+_56;if(typeof _29[key]=="undefined"){return -1;}else{return _29[key];}},useLegacyEvent:function(el,_58){if(!el.addEventListener&&!el.attachEvent){return true;}else{if(this.isSafari){if("click"==_58||"dblclick"==_58){return true;}}}return false;},removeListener:function(el,_59,fn){var i,len;if(typeof el=="string"){el=this.getEl(el);}else{if(this._isValidCollection(el)){var ok=true;for(i=0,len=el.length;i<len;++i){ok=(this.removeListener(el[i],_59,fn)&&ok);}return ok;}}if(!fn||!fn.call){return this.purgeElement(el,false,_59);}if("unload"==_59){for(i=0,len=_24.length;i<len;i++){var li=_24[i];if(li&&li[0]==el&&li[1]==_59&&li[2]==fn){_24.splice(i,1);return true;}}return false;}var _60=null;var _61=arguments[3];if("undefined"==typeof _61){_61=this._getCacheIndex(el,_59,fn);}if(_61>=0){_60=_23[_61];}if(!el||!_60){return false;}if(this.useLegacyEvent(el,_59)){var _62=this.getLegacyIndex(el,_59);var _63=_26[_62];if(_63){for(i=0,len=_63.length;i<len;++i){li=_63[i];if(li&&li[this.EL]==el&&li[this.TYPE]==_59&&li[this.FN]==fn){_63.splice(i,1);break;}}}}else{this._simpleRemove(el,_59,_60[this.WFN],false);}delete _23[_61][this.WFN];delete _23[_61][this.FN];_23.splice(_61,1);return true;},getTarget:function(ev,_65){var t=ev.target||ev.srcElement;return this.resolveTextNode(t);},resolveTextNode:function(_67){if(_67&&3==_67.nodeType){return _67.parentNode;}else{return _67;}},getPageX:function(ev){var x=ev.pageX;if(!x&&0!==x){x=ev.clientX||0;if(this.isIE){x+=this._getScrollLeft();}}return x;},getPageY:function(ev){var y=ev.pageY;if(!y&&0!==y){y=ev.clientY||0;if(this.isIE){y+=this._getScrollTop();}}return y;},getXY:function(ev){return [this.getPageX(ev),this.getPageY(ev)];},getRelatedTarget:function(ev){var t=ev.relatedTarget;if(!t){if(ev.type=="mouseout"){t=ev.toElement;}else{if(ev.type=="mouseover"){t=ev.fromElement;}}}return this.resolveTextNode(t);},getTime:function(ev){if(!ev.time){var t=new Date().getTime();try{ev.time=t;}catch(e){return t;}}return ev.time;},stopEvent:function(ev){this.stopPropagation(ev);this.preventDefault(ev);},stopPropagation:function(ev){if(ev.stopPropagation){ev.stopPropagation();}else{ev.cancelBubble=true;}},preventDefault:function(ev){if(ev.preventDefault){ev.preventDefault();}else{ev.returnValue=false;}},getEvent:function(e){var ev=e||window.event;if(!ev){var c=this.getEvent.caller;while(c){ev=c.arguments[0];if(ev&&Event==ev.constructor){break;}c=c.caller;}}return ev;},getCharCode:function(ev){return ev.charCode||ev.keyCode||0;},_getCacheIndex:function(el,_71,fn){for(var i=0,len=_23.length;i<len;++i){var li=_23[i];if(li&&li[this.FN]==fn&&li[this.EL]==el&&li[this.TYPE]==_71){return i;}}return -1;},generateId:function(el){var id=el.id;if(!id){id="yuievtautoid-"+_30;++_30;el.id=id;}return id;},_isValidCollection:function(o){return (o&&o.length&&typeof o!="string"&&!o.tagName&&!o.alert&&typeof o[0]!="undefined");},elCache:{},getEl:function(id){return document.getElementById(id);},clearCache:function(){},_load:function(e){_22=true;var EU=YAHOO.util.Event;if(this.isIE){EU._simpleRemove(window,"load",EU._load);}},_tryPreloadAttach:function(){if(this.locked){return false;}this.locked=true;var _75=!_22;if(!_75){_75=(_27>0);}var _76=[];for(var i=0,len=_28.length;i<len;++i){var _77=_28[i];if(_77){var el=this.getEl(_77.id);if(el){if(!_77.checkReady||_22||el.nextSibling||(document&&document.body)){var _78=el;if(_77.override){if(_77.override===true){_78=_77.obj;}else{_78=_77.override;}}_77.fn.call(_78,_77.obj);delete _28[i];}}else{_76.push(_77);}}}_27=(_76.length===0)?0:_27-1;if(_75){this.startInterval();}else{clearInterval(this._interval);this._interval=null;}this.locked=false;return true;},purgeElement:function(el,_79,_80){var _81=this.getListeners(el,_80);if(_81){for(var i=0,len=_81.length;i<len;++i){var l=_81[i];this.removeListener(el,l.type,l.fn);}}if(_79&&el&&el.childNodes){for(i=0,len=el.childNodes.length;i<len;++i){this.purgeElement(el.childNodes[i],_79,_80);}}},getListeners:function(el,_83){var _84=[];if(_23&&_23.length>0){for(var i=0,len=_23.length;i<len;++i){var l=_23[i];if(l&&l[this.EL]===el&&(!_83||_83===l[this.TYPE])){_84.push({type:l[this.TYPE],fn:l[this.FN],obj:l[this.OBJ],adjust:l[this.ADJ_SCOPE],index:i});}}}return (_84.length)?_84:null;},_unload:function(e){var EU=YAHOO.util.Event,i,j,l,len,index;for(i=0,len=_24.length;i<len;++i){l=_24[i];if(l){var _85=window;if(l[EU.ADJ_SCOPE]){if(l[EU.ADJ_SCOPE]===true){_85=l[EU.OBJ];}else{_85=l[EU.ADJ_SCOPE];}}l[EU.FN].call(_85,EU.getEvent(e),l[EU.OBJ]);delete _24[i];l=null;_85=null;}}if(_23&&_23.length>0){j=_23.length;while(j){index=j-1;l=_23[index];if(l){EU.removeListener(l[EU.EL],l[EU.TYPE],l[EU.FN],index);}j=j-1;}l=null;EU.clearCache();}for(i=0,len=_25.length;i<len;++i){delete _25[i][0];delete _25[i];}EU._simpleRemove(window,"unload",EU._unload);},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var dd=document.documentElement,db=document.body;if(dd&&(dd.scrollTop||dd.scrollLeft)){return [dd.scrollTop,dd.scrollLeft];}else{if(db){return [db.scrollTop,db.scrollLeft];}else{return [0,0];}}},_simpleAdd:function(){if(window.addEventListener){return function(el,_87,fn,_88){el.addEventListener(_87,fn,(_88));};}else{if(window.attachEvent){return function(el,_89,fn,_90){el.attachEvent("on"+_89,fn);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(el,_91,fn,_92){el.removeEventListener(_91,fn,(_92));};}else{if(window.detachEvent){return function(el,_93,fn){el.detachEvent("on"+_93,fn);};}else{return function(){};}}}()};}();(function(){var EU=YAHOO.util.Event;EU.on=EU.addListener;if(document&&document.body){EU._load();}else{EU._simpleAdd(window,"load",EU._load);}EU._simpleAdd(window,"unload",EU._unload);EU._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(_94,_95,_96,_97){this.__yui_events=this.__yui_events||{};var ce=this.__yui_events[_94];if(ce){ce.subscribe(_95,_96,_97);}else{this.__yui_subscribers=this.__yui_subscribers||{};var _99=this.__yui_subscribers;if(!_99[_94]){_99[_94]=[];}_99[_94].push({fn:_95,obj:_96,override:_97});}},unsubscribe:function(_100,p_fn,_102){this.__yui_events=this.__yui_events||{};var ce=this.__yui_events[_100];if(ce){return ce.unsubscribe(p_fn,_102);}else{return false;}},createEvent:function(_103,_104){this.__yui_events=this.__yui_events||{};var opts=_104||{};var _106=this.__yui_events;if(_106[_103]){}else{var _107=opts.scope||this;var _108=opts.silent||null;var ce=new YAHOO.util.CustomEvent(_103,_107,_108,YAHOO.util.CustomEvent.FLAT);_106[_103]=ce;if(opts.onSubscribeCallback){ce.subscribeEvent.subscribe(opts.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};var qs=this.__yui_subscribers[_103];if(qs){for(var i=0;i<qs.length;++i){ce.subscribe(qs[i].fn,qs[i].obj,qs[i].override);}}}return _106[_103];},fireEvent:function(_110,arg1,arg2,etc){this.__yui_events=this.__yui_events||{};var ce=this.__yui_events[_110];if(ce){var args=[];for(var i=1;i<arguments.length;++i){args.push(arguments[i]);}return ce.fire.apply(ce,args);}else{return null;}},hasEvent:function(type){if(this.__yui_events){if(this.__yui_events[type]){return true;}}return false;}};
/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */(function(){var Y=YAHOO.util,getStyle,setStyle,id_counter=0,propertyCache={};var ua=navigator.userAgent.toLowerCase(),isOpera=(ua.indexOf('opera')>-1),isSafari=(ua.indexOf('safari')>-1),isGecko=(!isOpera&&!isSafari&&ua.indexOf('gecko')>-1),isIE=(!isOpera&&ua.indexOf('msie')>-1);var patterns={HYPHEN:/(-[a-z])/i};var toCamel=function(property){if(!patterns.HYPHEN.test(property)){return property;}if(propertyCache[property]){return propertyCache[property];}while(patterns.HYPHEN.exec(property)){property=property.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());}propertyCache[property]=property;return property;};if(document.defaultView&&document.defaultView.getComputedStyle){getStyle=function(el,property){var value=null;var computed=document.defaultView.getComputedStyle(el,'');if(computed){value=computed[toCamel(property)];}return el.style[property]||value;};}else if(document.documentElement.currentStyle&&isIE){getStyle=function(el,property){switch(toCamel(property)){case'opacity':var val=100;try{val=el.filters['DXImageTransform.Microsoft.Alpha'].opacity;}catch(e){try{val=el.filters('alpha').opacity;}catch(e){}}return val/100;break;default:var value=el.currentStyle?el.currentStyle[property]:null;return(el.style[property]||value);}};}else{getStyle=function(el,property){return el.style[property];};}if(isIE){setStyle=function(el,property,val){switch(property){case'opacity':if(typeof el.style.filter=='string'){el.style.filter='alpha(opacity='+val*100+')';if(!el.currentStyle||!el.currentStyle.hasLayout){el.style.zoom=1;}}break;default:el.style[property]=val;}};}else{setStyle=function(el,property,val){el.style[property]=val;};}YAHOO.util.Dom={get:function(el){if(!el){return null;}if(typeof el!='string'&&!(el instanceof Array)){return el;}if(typeof el=='string'){return document.getElementById(el);}else{var collection=[];for(var i=0,len=el.length;i<len;++i){collection[collection.length]=Y.Dom.get(el[i]);}return collection;}return null;},getStyle:function(el,property){property=toCamel(property);var f=function(element){return getStyle(element,property);};return Y.Dom.batch(el,f,Y.Dom,true);},setStyle:function(el,property,val){property=toCamel(property);var f=function(element){setStyle(element,property,val);};Y.Dom.batch(el,f,Y.Dom,true);},getXY:function(el){var f=function(el){if(el.parentNode===null||el.offsetParent===null||this.getStyle(el,'display')=='none'){return false;}var parentNode=null;var pos=[];var box;if(el.getBoundingClientRect){box=el.getBoundingClientRect();var doc=document;if(!this.inDocument(el)&&parent.document!=document){doc=parent.document;if(!this.isAncestor(doc.documentElement,el)){return false;}}var scrollTop=Math.max(doc.documentElement.scrollTop,doc.body.scrollTop);var scrollLeft=Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft);return[box.left+scrollLeft,box.top+scrollTop];}else{pos=[el.offsetLeft,el.offsetTop];parentNode=el.offsetParent;if(parentNode!=el){while(parentNode){pos[0]+=parentNode.offsetLeft;pos[1]+=parentNode.offsetTop;parentNode=parentNode.offsetParent;}}if(isSafari&&this.getStyle(el,'position')=='absolute'){pos[0]-=document.body.offsetLeft;pos[1]-=document.body.offsetTop;}}if(el.parentNode){parentNode=el.parentNode;}else{parentNode=null;}while(parentNode&&parentNode.tagName.toUpperCase()!='BODY'&&parentNode.tagName.toUpperCase()!='HTML'){if(Y.Dom.getStyle(parentNode,'display')!='inline'){pos[0]-=parentNode.scrollLeft;pos[1]-=parentNode.scrollTop;}if(parentNode.parentNode){parentNode=parentNode.parentNode;}else{parentNode=null;}}return pos;};return Y.Dom.batch(el,f,Y.Dom,true);},getX:function(el){var f=function(el){return Y.Dom.getXY(el)[0];};return Y.Dom.batch(el,f,Y.Dom,true);},getY:function(el){var f=function(el){return Y.Dom.getXY(el)[1];};return Y.Dom.batch(el,f,Y.Dom,true);},setXY:function(el,pos,noRetry){var f=function(el){var style_pos=this.getStyle(el,'position');if(style_pos=='static'){this.setStyle(el,'position','relative');style_pos='relative';}var pageXY=this.getXY(el);if(pageXY===false){return false;}var delta=[parseInt(this.getStyle(el,'left'),10),parseInt(this.getStyle(el,'top'),10)];if(isNaN(delta[0])){delta[0]=(style_pos=='relative')?0:el.offsetLeft;}if(isNaN(delta[1])){delta[1]=(style_pos=='relative')?0:el.offsetTop;}if(pos[0]!==null){el.style.left=pos[0]-pageXY[0]+delta[0]+'px';}if(pos[1]!==null){el.style.top=pos[1]-pageXY[1]+delta[1]+'px';}var newXY=this.getXY(el);if(!noRetry&&(newXY[0]!=pos[0]||newXY[1]!=pos[1])){this.setXY(el,pos,true);}};Y.Dom.batch(el,f,Y.Dom,true);},setX:function(el,x){Y.Dom.setXY(el,[x,null]);},setY:function(el,y){Y.Dom.setXY(el,[null,y]);},getRegion:function(el){var f=function(el){var region=new Y.Region.getRegion(el);return region;};return Y.Dom.batch(el,f,Y.Dom,true);},getClientWidth:function(){return Y.Dom.getViewportWidth();},getClientHeight:function(){return Y.Dom.getViewportHeight();},getElementsByClassName:function(className,tag,root){var method=function(el){return Y.Dom.hasClass(el,className);};return Y.Dom.getElementsBy(method,tag,root);},hasClass:function(el,className){var re=new RegExp('(?:^|\\s+)'+className+'(?:\\s+|$)');var f=function(el){return re.test(el['className']);};return Y.Dom.batch(el,f,Y.Dom,true);},addClass:function(el,className){var f=function(el){if(this.hasClass(el,className)){return;}el['className']=[el['className'],className].join(' ');};Y.Dom.batch(el,f,Y.Dom,true);},removeClass:function(el,className){var re=new RegExp('(?:^|\\s+)'+className+'(?:\\s+|$)','g');var f=function(el){if(!this.hasClass(el,className)){return;}var c=el['className'];el['className']=c.replace(re,' ');if(this.hasClass(el,className)){this.removeClass(el,className);}};Y.Dom.batch(el,f,Y.Dom,true);},replaceClass:function(el,oldClassName,newClassName){if(oldClassName===newClassName){return false;}var re=new RegExp('(?:^|\\s+)'+oldClassName+'(?:\\s+|$)','g');var f=function(el){if(!this.hasClass(el,oldClassName)){this.addClass(el,newClassName);return;}el['className']=el['className'].replace(re,' '+newClassName+' ');if(this.hasClass(el,oldClassName)){this.replaceClass(el,oldClassName,newClassName);}};Y.Dom.batch(el,f,Y.Dom,true);},generateId:function(el,prefix){prefix=prefix||'yui-gen';el=el||{};var f=function(el){if(el){el=Y.Dom.get(el);}else{el={};}if(!el.id){el.id=prefix+id_counter++;}return el.id;};return Y.Dom.batch(el,f,Y.Dom,true);},isAncestor:function(haystack,needle){haystack=Y.Dom.get(haystack);if(!haystack||!needle){return false;}var f=function(needle){if(haystack.contains&&!isSafari){return haystack.contains(needle);}else if(haystack.compareDocumentPosition){return!!(haystack.compareDocumentPosition(needle)&16);}else{var parent=needle.parentNode;while(parent){if(parent==haystack){return true;}else if(!parent.tagName||parent.tagName.toUpperCase()=='HTML'){return false;}parent=parent.parentNode;}return false;}};return Y.Dom.batch(needle,f,Y.Dom,true);},inDocument:function(el){var f=function(el){return this.isAncestor(document.documentElement,el);};return Y.Dom.batch(el,f,Y.Dom,true);},getElementsBy:function(method,tag,root){tag=tag||'*';root=Y.Dom.get(root)||document;var nodes=[];var elements=root.getElementsByTagName(tag);if(!elements.length&&(tag=='*'&&root.all)){elements=root.all;}for(var i=0,len=elements.length;i<len;++i){if(method(elements[i])){nodes[nodes.length]=elements[i];}}return nodes;},batch:function(el,method,o,override){var id=el;el=Y.Dom.get(el);var scope=(override)?o:window;if(!el||el.tagName||!el.length){if(!el){return false;}return method.call(scope,el,o);}var collection=[];for(var i=0,len=el.length;i<len;++i){if(!el[i]){id=el[i];}collection[collection.length]=method.call(scope,el[i],o);}return collection;},getDocumentHeight:function(){var scrollHeight=(document.compatMode!='CSS1Compat')?document.body.scrollHeight:document.documentElement.scrollHeight;var h=Math.max(scrollHeight,Y.Dom.getViewportHeight());return h;},getDocumentWidth:function(){var scrollWidth=(document.compatMode!='CSS1Compat')?document.body.scrollWidth:document.documentElement.scrollWidth;var w=Math.max(scrollWidth,Y.Dom.getViewportWidth());return w;},getViewportHeight:function(){var height=self.innerHeight;var mode=document.compatMode;if((mode||isIE)&&!isOpera){height=(mode=='CSS1Compat')?document.documentElement.clientHeight:document.body.clientHeight;}return height;},getViewportWidth:function(){var width=self.innerWidth;var mode=document.compatMode;if(mode||isIE){width=(mode=='CSS1Compat')?document.documentElement.clientWidth:document.body.clientWidth;}return width;}};})();YAHOO.util.Region=function(t,r,b,l){this.top=t;this[1]=t;this.right=r;this.bottom=b;this.left=l;this[0]=l;};YAHOO.util.Region.prototype.contains=function(region){return(region.left>=this.left&&region.right<=this.right&&region.top>=this.top&&region.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(region){var t=Math.max(this.top,region.top);var r=Math.min(this.right,region.right);var b=Math.min(this.bottom,region.bottom);var l=Math.max(this.left,region.left);if(b>=t&&r>=l){return new YAHOO.util.Region(t,r,b,l);}else{return null;}};YAHOO.util.Region.prototype.union=function(region){var t=Math.min(this.top,region.top);var r=Math.max(this.right,region.right);var b=Math.max(this.bottom,region.bottom);var l=Math.min(this.left,region.left);return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(el){var p=YAHOO.util.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};YAHOO.util.Point.prototype=new YAHOO.util.Region();
/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */
YAHOO.util.Connect={_msxml_progid:['MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'],_http_header:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:'application/x-www-form-urlencoded',_isFormSubmit:false,_isFileUpload:false,_formNode:null,_sFormData:null,_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,setProgId:function(id)
{this._msxml_progid.unshift(id);},setDefaultPostHeader:function(b)
{this._use_default_post_header=b;},setPollingInterval:function(i)
{if(typeof i=='number'&&isFinite(i)){this._polling_interval=i;}},createXhrObject:function(transactionId)
{var obj,http;try
{http=new XMLHttpRequest();obj={conn:http,tId:transactionId};}
catch(e)
{for(var i=0;i<this._msxml_progid.length;++i){try
{http=new ActiveXObject(this._msxml_progid[i]);obj={conn:http,tId:transactionId};break;}
catch(e){}}}
finally
{return obj;}},getConnectionObject:function()
{var o;var tId=this._transaction_id;try
{o=this.createXhrObject(tId);if(o){this._transaction_id++;}}
catch(e){}
finally
{return o;}},asyncRequest:function(method,uri,callback,postData)
{var o=this.getConnectionObject();if(!o){return null;}
else{if(this._isFormSubmit){if(this._isFileUpload){this.uploadFile(o.tId,callback,uri,postData);this.releaseObject(o);return;}
if(method=='GET'){if(this._sFormData.length!=0){uri+=((uri.indexOf('?')==-1)?'?':'&')+this._sFormData;}
else{uri+="?"+this._sFormData;}}
else if(method=='POST'){postData=postData?this._sFormData+"&"+postData:this._sFormData;}}
o.conn.open(method,uri,true);if(this._isFormSubmit||(postData&&this._use_default_post_header)){this.initHeader('Content-Type',this._default_post_header);if(this._isFormSubmit){this.resetFormState();}}
if(this._has_http_headers){this.setHeader(o);}
this.handleReadyState(o,callback);o.conn.send(postData||null);return o;}},handleReadyState:function(o,callback)
{var oConn=this;if(callback&&callback.timeout){this._timeOut[o.tId]=window.setTimeout(function(){oConn.abort(o,callback,true);},callback.timeout);}
this._poll[o.tId]=window.setInterval(function(){if(o.conn&&o.conn.readyState==4){window.clearInterval(oConn._poll[o.tId]);delete oConn._poll[o.tId];if(callback&&callback.timeout){delete oConn._timeOut[o.tId];}
oConn.handleTransactionResponse(o,callback);}},this._polling_interval);},handleTransactionResponse:function(o,callback,isAbort)
{if(!callback){this.releaseObject(o);return;}
var httpStatus,responseObject;try
{if(o.conn.status!==undefined&&o.conn.status!=0){httpStatus=o.conn.status;}
else{httpStatus=13030;}}
catch(e){httpStatus=13030;}
if(httpStatus>=200&&httpStatus<300){try
{responseObject=this.createResponseObject(o,callback.argument);if(callback.success){if(!callback.scope){callback.success(responseObject);}
else{callback.success.apply(callback.scope,[responseObject]);}}}
catch(e){}}
else{try
{switch(httpStatus){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:responseObject=this.createExceptionObject(o.tId,callback.argument,(isAbort?isAbort:false));if(callback.failure){if(!callback.scope){callback.failure(responseObject);}
else{callback.failure.apply(callback.scope,[responseObject]);}}
break;default:responseObject=this.createResponseObject(o,callback.argument);if(callback.failure){if(!callback.scope){callback.failure(responseObject);}
else{callback.failure.apply(callback.scope,[responseObject]);}}}}
catch(e){}}
this.releaseObject(o);responseObject=null;},createResponseObject:function(o,callbackArg)
{var obj={};var headerObj={};try
{var headerStr=o.conn.getAllResponseHeaders();var header=headerStr.split('\n');for(var i=0;i<header.length;i++){var delimitPos=header[i].indexOf(':');if(delimitPos!=-1){headerObj[header[i].substring(0,delimitPos)]=header[i].substring(delimitPos+2);}}}
catch(e){}
obj.tId=o.tId;obj.status=o.conn.status;obj.statusText=o.conn.statusText;obj.getResponseHeader=headerObj;obj.getAllResponseHeaders=headerStr;obj.responseText=o.conn.responseText;obj.responseXML=o.conn.responseXML;if(typeof callbackArg!==undefined){obj.argument=callbackArg;}
return obj;},createExceptionObject:function(tId,callbackArg,isAbort)
{var COMM_CODE=0;var COMM_ERROR='communication failure';var ABORT_CODE=-1;var ABORT_ERROR='transaction aborted';var obj={};obj.tId=tId;if(isAbort){obj.status=ABORT_CODE;obj.statusText=ABORT_ERROR;}
else{obj.status=COMM_CODE;obj.statusText=COMM_ERROR;}
if(callbackArg){obj.argument=callbackArg;}
return obj;},initHeader:function(label,value)
{if(this._http_header[label]===undefined){this._http_header[label]=value;}
else{this._http_header[label]=value+","+this._http_header[label];}
this._has_http_headers=true;},setHeader:function(o)
{for(var prop in this._http_header){if(this._http_header.hasOwnProperty(prop)){o.conn.setRequestHeader(prop,this._http_header[prop]);}}
delete this._http_header;this._http_header={};this._has_http_headers=false;},setForm:function(formId,isUpload,secureUri)
{this.resetFormState();var oForm;if(typeof formId=='string'){oForm=(document.getElementById(formId)||document.forms[formId]);}
else if(typeof formId=='object'){oForm=formId;}
else{return;}
if(isUpload){this.createFrame(secureUri?secureUri:null);this._isFormSubmit=true;this._isFileUpload=true;this._formNode=oForm;return;}
var oElement,oName,oValue,oDisabled;var hasSubmit=false;for(var i=0;i<oForm.elements.length;i++){oElement=oForm.elements[i];oDisabled=oForm.elements[i].disabled;oName=oForm.elements[i].name;oValue=oForm.elements[i].value;if(!oDisabled&&oName)
{switch(oElement.type)
{case'select-one':case'select-multiple':for(var j=0;j<oElement.options.length;j++){if(oElement.options[j].selected){if(window.ActiveXObject){this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text)+'&';}
else{this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text)+'&';}}}
break;case'radio':case'checkbox':if(oElement.checked){this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oValue)+'&';}
break;case'file':case undefined:case'reset':case'button':break;case'submit':if(hasSubmit==false){this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oValue)+'&';hasSubmit=true;}
break;default:this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oValue)+'&';break;}}}
this._isFormSubmit=true;this._sFormData=this._sFormData.substr(0,this._sFormData.length-1);return this._sFormData;},resetFormState:function(){this._isFormSubmit=false;this._isFileUpload=false;this._formNode=null;this._sFormData="";},createFrame:function(secureUri){var frameId='yuiIO'+this._transaction_id;if(window.ActiveXObject){var io=document.createElement('<iframe id="'+frameId+'" name="'+frameId+'" />');if(typeof secureUri=='boolean'){io.src='javascript:false';}
else if(typeof secureURI=='string'){io.src=secureUri;}}
else{var io=document.createElement('iframe');io.id=frameId;io.name=frameId;}
io.style.position='absolute';io.style.top='-1000px';io.style.left='-1000px';document.body.appendChild(io);},appendPostData:function(postData)
{var formElements=new Array();var postMessage=postData.split('&');for(var i=0;i<postMessage.length;i++){var delimitPos=postMessage[i].indexOf('=');if(delimitPos!=-1){formElements[i]=document.createElement('input');formElements[i].type='hidden';formElements[i].name=postMessage[i].substring(0,delimitPos);formElements[i].value=postMessage[i].substring(delimitPos+1);this._formNode.appendChild(formElements[i]);}}
return formElements;},uploadFile:function(id,callback,uri,postData){var frameId='yuiIO'+id;var io=document.getElementById(frameId);this._formNode.action=uri;this._formNode.method='POST';this._formNode.target=frameId;if(this._formNode.encoding){this._formNode.encoding='multipart/form-data';}
else{this._formNode.enctype='multipart/form-data';}
if(postData){var oElements=this.appendPostData(postData);}
this._formNode.submit();if(oElements&&oElements.length>0){try
{for(var i=0;i<oElements.length;i++){this._formNode.removeChild(oElements[i]);}}
catch(e){}}
this.resetFormState();var uploadCallback=function()
{var obj={};obj.tId=id;obj.argument=callback.argument;try
{obj.responseText=io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;obj.responseXML=io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;}
catch(e){}
if(callback.upload){if(!callback.scope){callback.upload(obj);}
else{callback.upload.apply(callback.scope,[obj]);}}
if(YAHOO.util.Event){YAHOO.util.Event.removeListener(io,"load",uploadCallback);}
else if(window.detachEvent){io.detachEvent('onload',uploadCallback);}
else{io.removeEventListener('load',uploadCallback,false);}
setTimeout(function(){document.body.removeChild(io);},100);};if(YAHOO.util.Event){YAHOO.util.Event.addListener(io,"load",uploadCallback);}
else if(window.attachEvent){io.attachEvent('onload',uploadCallback);}
else{io.addEventListener('load',uploadCallback,false);}},abort:function(o,callback,isTimeout)
{if(this.isCallInProgress(o)){o.conn.abort();window.clearInterval(this._poll[o.tId]);delete this._poll[o.tId];if(isTimeout){delete this._timeOut[o.tId];}
this.handleTransactionResponse(o,callback,true);return true;}
else{return false;}},isCallInProgress:function(o)
{if(o.conn){return o.conn.readyState!=4&&o.conn.readyState!=0;}
else{return false;}},releaseObject:function(o)
{o.conn=null;o=null;}};
/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version 0.12.0
*/

/**
* Config is a utility used within an Object to allow the implementer to maintain a list of local configuration properties and listen for changes to those properties dynamically using CustomEvent. The initial values are also maintained so that the configuration can be reset at any given point to its initial state.
* @class YAHOO.util.Config
* @constructor
* @param {Object}	owner	The owner Object to which this Config Object belongs
*/
YAHOO.util.Config = function(owner) {
	if (owner) {
		this.init(owner);
	}
};

YAHOO.util.Config.prototype = {

	/**
	* Object reference to the owner of this Config Object
	* @property owner
	* @type Object
	*/
	owner : null,

	/**
	* Boolean flag that specifies whether a queue is currently being executed
	* @property queueInProgress
	* @type Boolean
	*/
	queueInProgress : false,


	/**
	* Validates that the value passed in is a Boolean.
	* @method checkBoolean
	* @param	{Object}	val	The value to validate
	* @return	{Boolean}	true, if the value is valid
	*/
	checkBoolean: function(val) {
		if (typeof val == 'boolean') {
			return true;
		} else {
			return false;
		}
	},

	/**
	* Validates that the value passed in is a number.
	* @method checkNumber
	* @param	{Object}	val	The value to validate
	* @return	{Boolean}	true, if the value is valid
	*/
	checkNumber: function(val) {
		if (isNaN(val)) {
			return false;
		} else {
			return true;
		}
	}
};


/**
* Initializes the configuration Object and all of its local members.
* @method init
* @param {Object}	owner	The owner Object to which this Config Object belongs
*/
YAHOO.util.Config.prototype.init = function(owner) {

	this.owner = owner;

	/**
	* Object reference to the owner of this Config Object
	* @event configChangedEvent
	*/
	this.configChangedEvent = new YAHOO.util.CustomEvent("configChanged");

	this.queueInProgress = false;

	/* Private Members */

	/**
	* Maintains the local collection of configuration property objects and their specified values
	* @property config
	* @private
	* @type Object
	*/
	var config = {};

	/**
	* Maintains the local collection of configuration property objects as they were initially applied.
	* This object is used when resetting a property.
	* @property initialConfig
	* @private
	* @type Object
	*/
	var initialConfig = {};

	/**
	* Maintains the local, normalized CustomEvent queue
	* @property eventQueue
	* @private
	* @type Object
	*/
	var eventQueue = [];

	/**
	* Fires a configuration property event using the specified value.
	* @method fireEvent
	* @private
	* @param {String}	key			The configuration property's name
	* @param {value}	Object		The value of the correct type for the property
	*/
	var fireEvent = function( key, value ) {
		key = key.toLowerCase();

		var property = config[key];

		if (typeof property != 'undefined' && property.event) {
			property.event.fire(value);
		}
	};
	/* End Private Members */

	/**
	* Adds a property to the Config Object's private config hash.
	* @method addProperty
	* @param {String}	key	The configuration property's name
	* @param {Object}	propertyObject	The Object containing all of this property's arguments
	*/
	this.addProperty = function( key, propertyObject ) {
		key = key.toLowerCase();

		config[key] = propertyObject;

		propertyObject.event = new YAHOO.util.CustomEvent(key);
		propertyObject.key = key;

		if (propertyObject.handler) {
			propertyObject.event.subscribe(propertyObject.handler, this.owner, true);
		}

		this.setProperty(key, propertyObject.value, true);

		if (! propertyObject.suppressEvent) {
			this.queueProperty(key, propertyObject.value);
		}
	};

	/**
	* Returns a key-value configuration map of the values currently set in the Config Object.
	* @method getConfig
	* @return {Object} The current config, represented in a key-value map
	*/
	this.getConfig = function() {
		var cfg = {};

		for (var prop in config) {
			var property = config[prop];
			if (typeof property != 'undefined' && property.event) {
				cfg[prop] = property.value;
			}
		}

		return cfg;
	};

	/**
	* Returns the value of specified property.
	* @method getProperty
	* @param {String} key	The name of the property
	* @return {Object}		The value of the specified property
	*/
	this.getProperty = function(key) {
		key = key.toLowerCase();

		var property = config[key];
		if (typeof property != 'undefined' && property.event) {
			return property.value;
		} else {
			return undefined;
		}
	};

	/**
	* Resets the specified property's value to its initial value.
	* @method resetProperty
	* @param {String} key	The name of the property
	* @return {Boolean} True is the property was reset, false if not
	*/
	this.resetProperty = function(key) {
		key = key.toLowerCase();

		var property = config[key];
		if (typeof property != 'undefined' && property.event) {
			if (initialConfig[key] && initialConfig[key] != 'undefined')	{
				this.setProperty(key, initialConfig[key]);
			}
			return true;
		} else {
			return false;
		}
	};

	/**
	* Sets the value of a property. If the silent property is passed as true, the property's event will not be fired.
	* @method setProperty
	* @param {String} key		The name of the property
	* @param {String} value		The value to set the property to
	* @param {Boolean} silent	Whether the value should be set silently, without firing the property event.
	* @return {Boolean}			True, if the set was successful, false if it failed.
	*/
	this.setProperty = function(key, value, silent) {
		key = key.toLowerCase();

		if (this.queueInProgress && ! silent) {
			this.queueProperty(key,value); // Currently running through a queue...
			return true;
		} else {
			var property = config[key];
			if (typeof property != 'undefined' && property.event) {
				if (property.validator && ! property.validator(value)) { // validator
					return false;
				} else {
					property.value = value;
					if (! silent) {
						fireEvent(key, value);
						this.configChangedEvent.fire([key, value]);
					}
					return true;
				}
			} else {
				return false;
			}
		}
	};

	/**
	* Sets the value of a property and queues its event to execute. If the event is already scheduled to execute, it is
	* moved from its current position to the end of the queue.
	* @method queueProperty
	* @param {String} key	The name of the property
	* @param {String} value	The value to set the property to
	* @return {Boolean}		true, if the set was successful, false if it failed.
	*/
	this.queueProperty = function(key, value) {
		key = key.toLowerCase();

		var property = config[key];

		if (typeof property != 'undefined' && property.event) {
			if (typeof value != 'undefined' && property.validator && ! property.validator(value)) { // validator
				return false;
			} else {

				if (typeof value != 'undefined') {
					property.value = value;
				} else {
					value = property.value;
				}

				var foundDuplicate = false;

				for (var i=0;i<eventQueue.length;i++) {
					var queueItem = eventQueue[i];

					if (queueItem) {
						var queueItemKey = queueItem[0];
						var queueItemValue = queueItem[1];

						if (queueItemKey.toLowerCase() == key) {
							// found a dupe... push to end of queue, null current item, and break
							eventQueue[i] = null;
							eventQueue.push([key, (typeof value != 'undefined' ? value : queueItemValue)]);
							foundDuplicate = true;
							break;
						}
					}
				}

				if (! foundDuplicate && typeof value != 'undefined') { // this is a refire, or a new property in the queue
					eventQueue.push([key, value]);
				}
			}

			if (property.supercedes) {
				for (var s=0;s<property.supercedes.length;s++) {
					var supercedesCheck = property.supercedes[s];

					for (var q=0;q<eventQueue.length;q++) {
						var queueItemCheck = eventQueue[q];

						if (queueItemCheck) {
							var queueItemCheckKey = queueItemCheck[0];
							var queueItemCheckValue = queueItemCheck[1];

							if ( queueItemCheckKey.toLowerCase() == supercedesCheck.toLowerCase() ) {
								eventQueue.push([queueItemCheckKey, queueItemCheckValue]);
								eventQueue[q] = null;
								break;
							}
						}
					}
				}
			}

			return true;
		} else {
			return false;
		}
	};

	/**
	* Fires the event for a property using the property's current value.
	* @method refireEvent
	* @param {String} key	The name of the property
	*/
	this.refireEvent = function(key) {
		key = key.toLowerCase();

		var property = config[key];
		if (typeof property != 'undefined' && property.event && typeof property.value != 'undefined') {
			if (this.queueInProgress) {
				this.queueProperty(key);
			} else {
				fireEvent(key, property.value);
			}
		}
	};

	/**
	* Applies a key-value Object literal to the configuration, replacing any existing values, and queueing the property events.
	* Although the values will be set, fireQueue() must be called for their associated events to execute.
	* @method applyConfig
	* @param {Object}	userConfig	The configuration Object literal
	* @param {Boolean}	init		When set to true, the initialConfig will be set to the userConfig passed in, so that calling a reset will reset the properties to the passed values.
	*/
	this.applyConfig = function(userConfig, init) {
		if (init) {
			initialConfig = userConfig;
		}
		for (var prop in userConfig) {
			this.queueProperty(prop, userConfig[prop]);
		}
	};

	/**
	* Refires the events for all configuration properties using their current values.
	* @method refresh
	*/
	this.refresh = function() {
		for (var prop in config) {
			this.refireEvent(prop);
		}
	};

	/**
	* Fires the normalized list of queued property change events
	* @method fireQueue
	*/
	this.fireQueue = function() {
		this.queueInProgress = true;
		for (var i=0;i<eventQueue.length;i++) {
			var queueItem = eventQueue[i];
			if (queueItem) {
				var key = queueItem[0];
				var value = queueItem[1];

				var property = config[key];
				property.value = value;

				fireEvent(key,value);
			}
		}

		this.queueInProgress = false;
		eventQueue = [];
	};

	/**
	* Subscribes an external handler to the change event for any given property.
	* @method subscribeToConfigEvent
	* @param {String}	key			The property name
	* @param {Function}	handler		The handler function to use subscribe to the property's event
	* @param {Object}	obj			The Object to use for scoping the event handler (see CustomEvent documentation)
	* @param {Boolean}	override	Optional. If true, will override "this" within the handler to map to the scope Object passed into the method.
	* @return {Boolean}				True, if the subscription was successful, otherwise false.
	*/
	this.subscribeToConfigEvent = function(key, handler, obj, override) {
		key = key.toLowerCase();

		var property = config[key];
		if (typeof property != 'undefined' && property.event) {
			if (! YAHOO.util.Config.alreadySubscribed(property.event, handler, obj)) {
				property.event.subscribe(handler, obj, override);
			}
			return true;
		} else {
			return false;
		}
	};

	/**
	* Unsubscribes an external handler from the change event for any given property.
	* @method unsubscribeFromConfigEvent
	* @param {String}	key			The property name
	* @param {Function}	handler		The handler function to use subscribe to the property's event
	* @param {Object}	obj			The Object to use for scoping the event handler (see CustomEvent documentation)
	* @return {Boolean}				True, if the unsubscription was successful, otherwise false.
	*/
	this.unsubscribeFromConfigEvent = function(key, handler, obj) {
		key = key.toLowerCase();

		var property = config[key];
		if (typeof property != 'undefined' && property.event) {
			return property.event.unsubscribe(handler, obj);
		} else {
			return false;
		}
	};

	/**
	* Returns a string representation of the Config object
	* @method toString
	* @return {String}	The Config object in string format.
	*/
	this.toString = function() {
		var output = "Config";
		if (this.owner) {
			output += " [" + this.owner.toString() + "]";
		}
		return output;
	};

	/**
	* Returns a string representation of the Config object's current CustomEvent queue
	* @method outputEventQueue
	* @return {String}	The string list of CustomEvents currently queued for execution
	*/
	this.outputEventQueue = function() {
		var output = "";
		for (var q=0;q<eventQueue.length;q++) {
			var queueItem = eventQueue[q];
			if (queueItem) {
				output += queueItem[0] + "=" + queueItem[1] + ", ";
			}
		}
		return output;
	};
};

/**
* Checks to determine if a particular function/Object pair are already subscribed to the specified CustomEvent
* @method YAHOO.util.Config.alreadySubscribed
* @static
* @param {YAHOO.util.CustomEvent} evt	The CustomEvent for which to check the subscriptions
* @param {Function}	fn	The function to look for in the subscribers list
* @param {Object}	obj	The execution scope Object for the subscription
* @return {Boolean}	true, if the function/Object pair is already subscribed to the CustomEvent passed in
*/
YAHOO.util.Config.alreadySubscribed = function(evt, fn, obj) {
	for (var e=0;e<evt.subscribers.length;e++) {
		var subsc = evt.subscribers[e];
		if (subsc && subsc.obj == obj && subsc.fn == fn) {
			return true;
		}
	}
	return false;
};

/**
*  The Container family of components is designed to enable developers to create different kinds of content-containing modules on the web. Module and Overlay are the most basic containers, and they can be used directly or extended to build custom containers. Also part of the Container family are four UI controls that extend Module and Overlay: Tooltip, Panel, Dialog, and SimpleDialog.
* @module Container
* @requires yahoo,dom,event,dragdrop,animation
*/

/**
* Module is a JavaScript representation of the Standard Module Format. Standard Module Format is a simple standard for markup containers where child nodes representing the header, body, and footer of the content are denoted using the CSS classes "hd", "bd", and "ft" respectively. Module is the base class for all other classes in the YUI Container package.
* @class Module
* @namespace YAHOO.widget
* @constructor
* @param {String} el			The element ID representing the Module <em>OR</em>
* @param {HTMLElement} el		The element representing the Module
* @param {Object} userConfig	The configuration Object literal containing the configuration that should be set for this module. See configuration documentation for more details.
*/
YAHOO.widget.Module = function(el, userConfig) {
	if (el) {
		this.init(el, userConfig);
	}
};

/**
* Constant representing the prefix path to use for non-secure images
* @property YAHOO.widget.Module.IMG_ROOT
* @static
* @final
* @type String
*/
YAHOO.widget.Module.IMG_ROOT = "http://us.i1.yimg.com/us.yimg.com/i/";

/**
* Constant representing the prefix path to use for securely served images
* @property YAHOO.widget.Module.IMG_ROOT_SSL
* @static
* @final
* @type String
*/
YAHOO.widget.Module.IMG_ROOT_SSL = "https://a248.e.akamai.net/sec.yimg.com/i/";

/**
* Constant for the default CSS class name that represents a Module
* @property YAHOO.widget.Module.CSS_MODULE
* @static
* @final
* @type String
*/
YAHOO.widget.Module.CSS_MODULE = "module";

/**
* Constant representing the module header
* @property YAHOO.widget.Module.CSS_HEADER
* @static
* @final
* @type String
*/
YAHOO.widget.Module.CSS_HEADER = "hd";

/**
* Constant representing the module body
* @property YAHOO.widget.Module.CSS_BODY
* @static
* @final
* @type String
*/
YAHOO.widget.Module.CSS_BODY = "bd";

/**
* Constant representing the module footer
* @property YAHOO.widget.Module.CSS_FOOTER
* @static
* @final
* @type String
*/
YAHOO.widget.Module.CSS_FOOTER = "ft";

/**
* Constant representing the url for the "src" attribute of the iframe used to monitor changes to the browser's base font size
* @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL
* @static
* @final
* @type String
*/
YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;";

YAHOO.widget.Module.prototype = {
	/**
	* The class's constructor function
	* @property contructor
	* @type Function
	*/
	constructor : YAHOO.widget.Module,

	/**
	* The main module element that contains the header, body, and footer
	* @property element
	* @type HTMLElement
	*/
	element : null,

	/**
	* The header element, denoted with CSS class "hd"
	* @property header
	* @type HTMLElement
	*/
	header : null,

	/**
	* The body element, denoted with CSS class "bd"
	* @property body
	* @type HTMLElement
	*/
	body : null,

	/**
	* The footer element, denoted with CSS class "ft"
	* @property footer
	* @type HTMLElement
	*/
	footer : null,

	/**
	* The id of the element
	* @property id
	* @type String
	*/
	id : null,

	/**
	* The String representing the image root
	* @property imageRoot
	* @type String
	*/
	imageRoot : YAHOO.widget.Module.IMG_ROOT,

	/**
	* Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
	* @method initEvents
	*/
	initEvents : function() {

		/**
		* CustomEvent fired prior to class initalization.
		* @event beforeInitEvent
		* @param {class} classRef	class reference of the initializing class, such as this.beforeInitEvent.fire(YAHOO.widget.Module)
		*/
		this.beforeInitEvent = new YAHOO.util.CustomEvent("beforeInit");

		/**
		* CustomEvent fired after class initalization.
		* @event initEvent
		* @param {class} classRef	class reference of the initializing class, such as this.beforeInitEvent.fire(YAHOO.widget.Module)
		*/
		this.initEvent = new YAHOO.util.CustomEvent("init");

		/**
		* CustomEvent fired when the Module is appended to the DOM
		* @event appendEvent
		*/
		this.appendEvent = new YAHOO.util.CustomEvent("append");

		/**
		* CustomEvent fired before the Module is rendered
		* @event beforeRenderEvent
		*/
		this.beforeRenderEvent = new YAHOO.util.CustomEvent("beforeRender");

		/**
		* CustomEvent fired after the Module is rendered
		* @event renderEvent
		*/
		this.renderEvent = new YAHOO.util.CustomEvent("render");

		/**
		* CustomEvent fired when the header content of the Module is modified
		* @event changeHeaderEvent
		* @param {String/HTMLElement} content	String/element representing the new header content
		*/
		this.changeHeaderEvent = new YAHOO.util.CustomEvent("changeHeader");

		/**
		* CustomEvent fired when the body content of the Module is modified
		* @event changeBodyEvent
		* @param {String/HTMLElement} content	String/element representing the new body content
		*/
		this.changeBodyEvent = new YAHOO.util.CustomEvent("changeBody");

		/**
		* CustomEvent fired when the footer content of the Module is modified
		* @event changeFooterEvent
		* @param {String/HTMLElement} content	String/element representing the new footer content
		*/
		this.changeFooterEvent = new YAHOO.util.CustomEvent("changeFooter");

		/**
		* CustomEvent fired when the content of the Module is modified
		* @event changeContentEvent
		*/
		this.changeContentEvent = new YAHOO.util.CustomEvent("changeContent");

		/**
		* CustomEvent fired when the Module is destroyed
		* @event destroyEvent
		*/
		this.destroyEvent = new YAHOO.util.CustomEvent("destroy");

		/**
		* CustomEvent fired before the Module is shown
		* @event beforeShowEvent
		*/
		this.beforeShowEvent = new YAHOO.util.CustomEvent("beforeShow");

		/**
		* CustomEvent fired after the Module is shown
		* @event showEvent
		*/
		this.showEvent = new YAHOO.util.CustomEvent("show");

		/**
		* CustomEvent fired before the Module is hidden
		* @event beforeHideEvent
		*/
		this.beforeHideEvent = new YAHOO.util.CustomEvent("beforeHide");

		/**
		* CustomEvent fired after the Module is hidden
		* @event hideEvent
		*/
		this.hideEvent = new YAHOO.util.CustomEvent("hide");
	},

	/**
	* String representing the current user-agent platform
	* @property platform
	* @type String
	*/
	platform : function() {
					var ua = navigator.userAgent.toLowerCase();
					if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) {
						return "windows";
					} else if (ua.indexOf("macintosh") != -1) {
						return "mac";
					} else {
						return false;
					}
				}(),

	/**
	* String representing the current user-agent browser
	* @property browser
	* @type String
	*/
	browser : function() {
			var ua = navigator.userAgent.toLowerCase();
				  if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
					 return 'opera';
				  } else if (ua.indexOf('msie 7')!=-1) { // IE7
					 return 'ie7';
				  } else if (ua.indexOf('msie') !=-1) { // IE
					 return 'ie';
				  } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
					 return 'safari';
				  } else if (ua.indexOf('gecko') != -1) { // Gecko
					 return 'gecko';
				  } else {
					 return false;
				  }
			}(),

	/**
	* Boolean representing whether or not the current browsing context is secure (https)
	* @property isSecure
	* @type Boolean
	*/
	isSecure : function() {
		if (window.location.href.toLowerCase().indexOf("https") === 0) {
			return true;
		} else {
			return false;
		}
	}(),

	/**
	* Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
	*/
	initDefaultConfig : function() {
		// Add properties //

		/**
		* Specifies whether the Module is visible on the page.
		* @config visible
		* @type Boolean
		* @default true
		*/
		this.cfg.addProperty("visible", { value:true, handler:this.configVisible, validator:this.cfg.checkBoolean } );

		/**
		* Object or array of objects representing the ContainerEffect classes that are active for animating the container.
		* @config effect
		* @type Object
		* @default null
		*/
		this.cfg.addProperty("effect", { suppressEvent:true, supercedes:["visible"] } );

		/**
		* Specifies whether to create a special proxy iframe to monitor for user font resizing in the document
		* @config monitorresize
		* @type Boolean
		* @default true
		*/
		this.cfg.addProperty("monitorresize", { value:true, handler:this.configMonitorResize } );
	},

	/**
	* The Module class's initialization method, which is executed for Module and all of its subclasses. This method is automatically called by the constructor, and  sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
	* @method init
	* @param {String}	el	The element ID representing the Module <em>OR</em>
	* @param {HTMLElement}	el	The element representing the Module
	* @param {Object}	userConfig	The configuration Object literal containing the configuration that should be set for this module. See configuration documentation for more details.
	*/
	init : function(el, userConfig) {

		this.initEvents();

		this.beforeInitEvent.fire(YAHOO.widget.Module);

		/**
		* The Module's Config object used for monitoring configuration properties.
		* @property cfg
		* @type YAHOO.util.Config
		*/
		this.cfg = new YAHOO.util.Config(this);

		if (this.isSecure) {
			this.imageRoot = YAHOO.widget.Module.IMG_ROOT_SSL;
		}

		if (typeof el == "string") {
			var elId = el;

			el = document.getElementById(el);
			if (! el) {
				el = document.createElement("DIV");
				el.id = elId;
			}
		}

		this.element = el;

		if (el.id) {
			this.id = el.id;
		}

		var childNodes = this.element.childNodes;

		if (childNodes) {
			for (var i=0;i<childNodes.length;i++) {
				var child = childNodes[i];
				switch (child.className) {
					case YAHOO.widget.Module.CSS_HEADER:
						this.header = child;
						break;
					case YAHOO.widget.Module.CSS_BODY:
						this.body = child;
						break;
					case YAHOO.widget.Module.CSS_FOOTER:
						this.footer = child;
						break;
				}
			}
		}

		this.initDefaultConfig();

		YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Module.CSS_MODULE);

		if (userConfig) {
			this.cfg.applyConfig(userConfig, true);
		}

		// Subscribe to the fireQueue() method of Config so that any queued configuration changes are
		// excecuted upon render of the Module
		if (! YAHOO.util.Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) {
			this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true);
		}

		this.initEvent.fire(YAHOO.widget.Module);
	},

	/**
	* Initialized an empty IFRAME that is placed out of the visible area that can be used to detect text resize.
	* @method initResizeMonitor
	*/
	initResizeMonitor : function() {

        if(this.browser != "opera") {

            var resizeMonitor = document.getElementById("_yuiResizeMonitor");

            if (! resizeMonitor) {

                resizeMonitor = document.createElement("iframe");

                var bIE = (this.browser.indexOf("ie") === 0);

                if(this.isSecure &&
                   YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL &&
                   bIE) {

                  resizeMonitor.src =
                       YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL;

                }

                resizeMonitor.id = "_yuiResizeMonitor";
                resizeMonitor.style.visibility = "hidden";

                document.body.appendChild(resizeMonitor);

                resizeMonitor.style.width = "10em";
                resizeMonitor.style.height = "10em";
                resizeMonitor.style.position = "absolute";

                var nLeft = -1 * resizeMonitor.offsetWidth,
                    nTop = -1 * resizeMonitor.offsetHeight;

                resizeMonitor.style.top = nTop + "px";
                resizeMonitor.style.left =  nLeft + "px";
                resizeMonitor.style.borderStyle = "none";
                resizeMonitor.style.borderWidth = "0";
                YAHOO.util.Dom.setStyle(resizeMonitor, "opacity", "0");

                resizeMonitor.style.visibility = "visible";

                if(!bIE) {

                    var doc = resizeMonitor.contentWindow.document;

                    doc.open();
                    doc.close();

                }

            }

            if(resizeMonitor && resizeMonitor.contentWindow) {

                this.resizeMonitor = resizeMonitor;

                YAHOO.util.Event.addListener(this.resizeMonitor.contentWindow, "resize", this.onDomResize, this, true);

            }

        }

	},

	/**
	* Event handler fired when the resize monitor element is resized.
	* @method onDomResize
	* @param {DOMEvent} e	The DOM resize event
	* @param {Object} obj	The scope object passed to the handler
	*/
	onDomResize : function(e, obj) {

        var nLeft = -1 * this.resizeMonitor.offsetWidth,
            nTop = -1 * this.resizeMonitor.offsetHeight;

        this.resizeMonitor.style.top = nTop + "px";
        this.resizeMonitor.style.left =  nLeft + "px";

	},

	/**
	* Sets the Module's header content to the HTML specified, or appends the passed element to the header. If no header is present, one will be automatically created.
	* @method setHeader
	* @param {String}	headerContent	The HTML used to set the header <em>OR</em>
	* @param {HTMLElement}	headerContent	The HTMLElement to append to the header
	*/
	setHeader : function(headerContent) {
		if (! this.header) {
			this.header = document.createElement("DIV");
			this.header.className = YAHOO.widget.Module.CSS_HEADER;
		}

		if (typeof headerContent == "string") {
			this.header.innerHTML = headerContent;
		} else {
			this.header.innerHTML = "";
			this.header.appendChild(headerContent);
		}

		this.changeHeaderEvent.fire(headerContent);
		this.changeContentEvent.fire();
	},

	/**
	* Appends the passed element to the header. If no header is present, one will be automatically created.
	* @method appendToHeader
	* @param {HTMLElement}	element	The element to append to the header
	*/
	appendToHeader : function(element) {
		if (! this.header) {
			this.header = document.createElement("DIV");
			this.header.className = YAHOO.widget.Module.CSS_HEADER;
		}

		this.header.appendChild(element);
		this.changeHeaderEvent.fire(element);
		this.changeContentEvent.fire();
	},

	/**
	* Sets the Module's body content to the HTML specified, or appends the passed element to the body. If no body is present, one will be automatically created.
	* @method setBody
	* @param {String}	bodyContent	The HTML used to set the body <em>OR</em>
	* @param {HTMLElement}	bodyContent	The HTMLElement to append to the body
	*/
	setBody : function(bodyContent) {
		if (! this.body) {
			this.body = document.createElement("DIV");
			this.body.className = YAHOO.widget.Module.CSS_BODY;
		}

		if (typeof bodyContent == "string")
		{
			this.body.innerHTML = bodyContent;
		} else {
			this.body.innerHTML = "";
			this.body.appendChild(bodyContent);
		}

		this.changeBodyEvent.fire(bodyContent);
		this.changeContentEvent.fire();
	},

	/**
	* Appends the passed element to the body. If no body is present, one will be automatically created.
	* @method appendToBody
	* @param {HTMLElement}	element	The element to append to the body
	*/
	appendToBody : function(element) {
		if (! this.body) {
			this.body = document.createElement("DIV");
			this.body.className = YAHOO.widget.Module.CSS_BODY;
		}

		this.body.appendChild(element);
		this.changeBodyEvent.fire(element);
		this.changeContentEvent.fire();
	},

	/**
	* Sets the Module's footer content to the HTML specified, or appends the passed element to the footer. If no footer is present, one will be automatically created.
	* @method setFooter
	* @param {String}	footerContent	The HTML used to set the footer <em>OR</em>
	* @param {HTMLElement}	footerContent	The HTMLElement to append to the footer
	*/
	setFooter : function(footerContent) {
		if (! this.footer) {
			this.footer = document.createElement("DIV");
			this.footer.className = YAHOO.widget.Module.CSS_FOOTER;
		}

		if (typeof footerContent == "string") {
			this.footer.innerHTML = footerContent;
		} else {
			this.footer.innerHTML = "";
			this.footer.appendChild(footerContent);
		}

		this.changeFooterEvent.fire(footerContent);
		this.changeContentEvent.fire();
	},

	/**
	* Appends the passed element to the footer. If no footer is present, one will be automatically created.
	* @method appendToFooter
	* @param {HTMLElement}	element	The element to append to the footer
	*/
	appendToFooter : function(element) {
		if (! this.footer) {
			this.footer = document.createElement("DIV");
			this.footer.className = YAHOO.widget.Module.CSS_FOOTER;
		}

		this.footer.appendChild(element);
		this.changeFooterEvent.fire(element);
		this.changeContentEvent.fire();
	},

	/**
	* Renders the Module by inserting the elements that are not already in the main Module into their correct places. Optionally appends the Module to the specified node prior to the render's execution. NOTE: For Modules without existing markup, the appendToNode argument is REQUIRED. If this argument is ommitted and the current element is not present in the document, the function will return false, indicating that the render was a failure.
	* @method render
	* @param {String}	appendToNode	The element id to which the Module should be appended to prior to rendering <em>OR</em>
	* @param {HTMLElement}	appendToNode	The element to which the Module should be appended to prior to rendering
	* @param {HTMLElement}	moduleElement	OPTIONAL. The element that represents the actual Standard Module container.
	* @return {Boolean} Success or failure of the render
	*/
	render : function(appendToNode, moduleElement) {
		this.beforeRenderEvent.fire();

		if (! moduleElement) {
			moduleElement = this.element;
		}

		var me = this;
		var appendTo = function(element) {
			if (typeof element == "string") {
				element = document.getElementById(element);
			}

			if (element) {
				element.appendChild(me.element);
				me.appendEvent.fire();
			}
		};

		if (appendToNode) {
			appendTo(appendToNode);
		} else { // No node was passed in. If the element is not pre-marked up, this fails
			if (! YAHOO.util.Dom.inDocument(this.element)) {
				return false;
			}
		}

		// Need to get everything into the DOM if it isn't already

		if (this.header && ! YAHOO.util.Dom.inDocument(this.header)) {
			// There is a header, but it's not in the DOM yet... need to add it
			var firstChild = moduleElement.firstChild;
			if (firstChild) { // Insert before first child if exists
				moduleElement.insertBefore(this.header, firstChild);
			} else { // Append to empty body because there are no children
				moduleElement.appendChild(this.header);
			}
		}

		if (this.body && ! YAHOO.util.Dom.inDocument(this.body)) {
			// There is a body, but it's not in the DOM yet... need to add it
			if (this.footer && YAHOO.util.Dom.isAncestor(this.moduleElement, this.footer)) { // Insert before footer if exists in DOM
				moduleElement.insertBefore(this.body, this.footer);
			} else { // Append to element because there is no footer
				moduleElement.appendChild(this.body);
			}
		}

		if (this.footer && ! YAHOO.util.Dom.inDocument(this.footer)) {
			// There is a footer, but it's not in the DOM yet... need to add it
			moduleElement.appendChild(this.footer);
		}

		this.renderEvent.fire();
		return true;
	},

	/**
	* Removes the Module element from the DOM and sets all child elements to null.
	* @method destroy
	*/
	destroy : function() {
		if (this.element) {
			var parent = this.element.parentNode;
		}
		if (parent) {
			parent.removeChild(this.element);
		}

		this.element = null;
		this.header = null;
		this.body = null;
		this.footer = null;

		this.destroyEvent.fire();
	},

	/**
	* Shows the Module element by setting the visible configuration property to true. Also fires two events: beforeShowEvent prior to the visibility change, and showEvent after.
	* @method show
	*/
	show : function() {
		this.cfg.setProperty("visible", true);
	},

	/**
	* Hides the Module element by setting the visible configuration property to false. Also fires two events: beforeHideEvent prior to the visibility change, and hideEvent after.
	* @method hide
	*/
	hide : function() {
		this.cfg.setProperty("visible", false);
	},

	// BUILT-IN EVENT HANDLERS FOR MODULE //

	/**
	* Default event handler for changing the visibility property of a Module. By default, this is achieved by switching the "display" style between "block" and "none".
	* This method is responsible for firing showEvent and hideEvent.
	* @param {String} type	The CustomEvent type (usually the property name)
	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
	* @method configVisible
	*/
	configVisible : function(type, args, obj) {
		var visible = args[0];
		if (visible) {
			this.beforeShowEvent.fire();
			YAHOO.util.Dom.setStyle(this.element, "display", "block");
			this.showEvent.fire();
		} else {
			this.beforeHideEvent.fire();
			YAHOO.util.Dom.setStyle(this.element, "display", "none");
			this.hideEvent.fire();
		}
	},

	/**
	* Default event handler for the "monitorresize" configuration property
	* @param {String} type	The CustomEvent type (usually the property name)
	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
	* @method configMonitorResize
	*/
	configMonitorResize : function(type, args, obj) {
		var monitor = args[0];
		if (monitor) {
			this.initResizeMonitor();
		} else {
			YAHOO.util.Event.removeListener(this.resizeMonitor, "resize", this.onDomResize);
			this.resizeMonitor = null;
		}
	}
};

/**
* Returns a String representation of the Object.
* @method toString
* @return {String}	The string representation of the Module
*/
YAHOO.widget.Module.prototype.toString = function() {
	return "Module " + this.id;
};

/**
* Overlay is a Module that is absolutely positioned above the page flow. It has convenience methods for positioning and sizing, as well as options for controlling zIndex and constraining the Overlay's position to the current visible viewport. Overlay also contains a dynamicly generated IFRAME which is placed beneath it for Internet Explorer 6 and 5.x so that it will be properly rendered above SELECT elements.
* @class Overlay
* @namespace YAHOO.widget
* @extends YAHOO.widget.Module
* @param {String}	el	The element ID representing the Overlay <em>OR</em>
* @param {HTMLElement}	el	The element representing the Overlay
* @param {Object}	userConfig	The configuration object literal containing 10/23/2006the configuration that should be set for this Overlay. See configuration documentation for more details.
* @constructor
*/
YAHOO.widget.Overlay = function(el, userConfig) {
	YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig);
};

YAHOO.extend(YAHOO.widget.Overlay, YAHOO.widget.Module);

/**
* The URL that will be placed in the iframe
* @property YAHOO.widget.Overlay.IFRAME_SRC
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.IFRAME_SRC = "javascript:false;"

/**
* Constant representing the top left corner of an element, used for configuring the context element alignment
* @property YAHOO.widget.Overlay.TOP_LEFT
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.TOP_LEFT = "tl";

/**
* Constant representing the top right corner of an element, used for configuring the context element alignment
* @property YAHOO.widget.Overlay.TOP_RIGHT
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.TOP_RIGHT = "tr";

/**
* Constant representing the top bottom left corner of an element, used for configuring the context element alignment
* @property YAHOO.widget.Overlay.BOTTOM_LEFT
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.BOTTOM_LEFT = "bl";

/**
* Constant representing the bottom right corner of an element, used for configuring the context element alignment
* @property YAHOO.widget.Overlay.BOTTOM_RIGHT
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.BOTTOM_RIGHT = "br";

/**
* Constant representing the default CSS class used for an Overlay
* @property YAHOO.widget.Overlay.CSS_OVERLAY
* @static
* @final
* @type String
*/
YAHOO.widget.Overlay.CSS_OVERLAY = "overlay";

/**
* The Overlay initialization method, which is executed for Overlay and all of its subclasses. This method is automatically called by the constructor, and  sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
* @method init
* @param {String}	el	The element ID representing the Overlay <em>OR</em>
* @param {HTMLElement}	el	The element representing the Overlay
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
*/
YAHOO.widget.Overlay.prototype.init = function(el, userConfig) {
	YAHOO.widget.Overlay.superclass.init.call(this, el/*, userConfig*/);  // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level

	this.beforeInitEvent.fire(YAHOO.widget.Overlay);

	YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Overlay.CSS_OVERLAY);

	if (userConfig) {
		this.cfg.applyConfig(userConfig, true);
	}

	if (this.platform == "mac" && this.browser == "gecko") {
		if (! YAHOO.util.Config.alreadySubscribed(this.showEvent,this.showMacGeckoScrollbars,this)) {
			this.showEvent.subscribe(this.showMacGeckoScrollbars,this,true);
		}
		if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent,this.hideMacGeckoScrollbars,this)) {
			this.hideEvent.subscribe(this.hideMacGeckoScrollbars,this,true);
		}
	}

	this.initEvent.fire(YAHOO.widget.Overlay);
};

/**
* Initializes the custom events for Overlay which are fired automatically at appropriate times by the Overlay class.
* @method initEvents
*/
YAHOO.widget.Overlay.prototype.initEvents = function() {
	YAHOO.widget.Overlay.superclass.initEvents.call(this);

	/**
	* CustomEvent fired before the Overlay is moved.
	* @event beforeMoveEvent
	* @param {Number} x	x coordinate
	* @param {Number} y	y coordinate
	*/
	this.beforeMoveEvent = new YAHOO.util.CustomEvent("beforeMove", this);

	/**
	* CustomEvent fired after the Overlay is moved.
	* @event moveEvent
	* @param {Number} x	x coordinate
	* @param {Number} y	y coordinate
	*/
	this.moveEvent = new YAHOO.util.CustomEvent("move", this);
};

/**
* Initializes the class's configurable properties which can be changed using the Overlay's Config object (cfg).
* @method initDefaultConfig
*/
YAHOO.widget.Overlay.prototype.initDefaultConfig = function() {
	YAHOO.widget.Overlay.superclass.initDefaultConfig.call(this);

	// Add overlay config properties //

	/**
	* The absolute x-coordinate position of the Overlay
	* @config x
	* @type Number
	* @default null
	*/
	this.cfg.addProperty("x", { handler:this.configX, validator:this.cfg.checkNumber, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* The absolute y-coordinate position of the Overlay
	* @config y
	* @type Number
	* @default null
	*/
	this.cfg.addProperty("y", { handler:this.configY, validator:this.cfg.checkNumber, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* An array with the absolute x and y positions of the Overlay
	* @config xy
	* @type Number[]
	* @default null
	*/
	this.cfg.addProperty("xy",{ handler:this.configXY, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* The array of context arguments for context-sensitive positioning. The format is: [id or element, element corner, context corner]. For example, setting this property to ["img1", "tl", "bl"] would align the Overlay's top left corner to the context element's bottom left corner.
	* @config context
	* @type Array
	* @default null
	*/
	this.cfg.addProperty("context",	{ handler:this.configContext, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* True if the Overlay should be anchored to the center of the viewport.
	* @config fixedcenter
	* @type Boolean
	* @default false
	*/
	this.cfg.addProperty("fixedcenter", { value:false, handler:this.configFixedCenter, validator:this.cfg.checkBoolean, supercedes:["iframe","visible"] } );

	/**
	* CSS width of the Overlay.
	* @config width
	* @type String
	* @default null
	*/
	this.cfg.addProperty("width", { handler:this.configWidth, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* CSS height of the Overlay.
	* @config height
	* @type String
	* @default null
	*/
	this.cfg.addProperty("height", { handler:this.configHeight, suppressEvent:true, supercedes:["iframe"] } );

	/**
	* CSS z-index of the Overlay.
	* @config zIndex
	* @type Number
	* @default null
	*/
	this.cfg.addProperty("zIndex", { value:null, handler:this.configzIndex } );

	/**
	* True if the Overlay should be prevented from being positioned out of the viewport.
	* @config constraintoviewport
	* @type Boolean
	* @default false
	*/
	this.cfg.addProperty("constraintoviewport", { value:false, handler:this.configConstrainToViewport, validator:this.cfg.checkBoolean, supercedes:["iframe","x","y","xy"] } );

	/**
	* True if the Overlay should have an IFRAME shim (for correcting the select z-index bug in IE6 and below).
	* @config iframe
	* @type Boolean
	* @default true for IE6 and below, false for all others
	*/
	this.cfg.addProperty("iframe", { value:(this.browser == "ie" ? true : false), handler:this.configIframe, validator:this.cfg.checkBoolean, supercedes:["zIndex"] } );
};

/**
* Moves the Overlay to the specified position. This function is identical to calling this.cfg.setProperty("xy", [x,y]);
* @method moveTo
* @param {Number}	x	The Overlay's new x position
* @param {Number}	y	The Overlay's new y position
*/
YAHOO.widget.Overlay.prototype.moveTo = function(x, y) {
	this.cfg.setProperty("xy",[x,y]);
};

/**
* Adds a special CSS class to the Overlay when Mac/Gecko is in use, to work around a Gecko bug where
* scrollbars cannot be hidden. See https://bugzilla.mozilla.org/show_bug.cgi?id=187435
* @method hideMacGeckoScrollbars
*/
YAHOO.widget.Overlay.prototype.hideMacGeckoScrollbars = function() {
	YAHOO.util.Dom.removeClass(this.element, "show-scrollbars");
	YAHOO.util.Dom.addClass(this.element, "hide-scrollbars");
};

/**
* Removes a special CSS class from the Overlay when Mac/Gecko is in use, to work around a Gecko bug where
* scrollbars cannot be hidden. See https://bugzilla.mozilla.org/show_bug.cgi?id=187435
* @method showMacGeckoScrollbars
*/
YAHOO.widget.Overlay.prototype.showMacGeckoScrollbars = function() {
	YAHOO.util.Dom.removeClass(this.element, "hide-scrollbars");
	YAHOO.util.Dom.addClass(this.element, "show-scrollbars");
};

// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //

/**
* The default event handler fired when the "visible" property is changed. This method is responsible for firing showEvent and hideEvent.
* @method configVisible
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configVisible = function(type, args, obj) {
	var visible = args[0];

	var currentVis = YAHOO.util.Dom.getStyle(this.element, "visibility");

	if (currentVis == "inherit") {
		var e = this.element.parentNode;
		while (e.nodeType != 9 && e.nodeType != 11) {
			currentVis = YAHOO.util.Dom.getStyle(e, "visibility");
			if (currentVis != "inherit") { break; }
			e = e.parentNode;
		}
		if (currentVis == "inherit") {
			currentVis = "visible";
		}
	}

	var effect = this.cfg.getProperty("effect");

	var effectInstances = [];
	if (effect) {
		if (effect instanceof Array) {
			for (var i=0;i<effect.length;i++) {
				var eff = effect[i];
				effectInstances[effectInstances.length] = eff.effect(this, eff.duration);
			}
		} else {
			effectInstances[effectInstances.length] = effect.effect(this, effect.duration);
		}
	}

	var isMacGecko = (this.platform == "mac" && this.browser == "gecko");

	if (visible) { // Show
		if (isMacGecko) {
			this.showMacGeckoScrollbars();
		}

		if (effect) { // Animate in
			if (visible) { // Animate in if not showing
				if (currentVis != "visible" || currentVis === "") {
					this.beforeShowEvent.fire();
					for (var j=0;j<effectInstances.length;j++) {
						var ei = effectInstances[j];
						if (j === 0 && ! YAHOO.util.Config.alreadySubscribed(ei.animateInCompleteEvent,this.showEvent.fire,this.showEvent)) {
							ei.animateInCompleteEvent.subscribe(this.showEvent.fire,this.showEvent,true); // Delegate showEvent until end of animateInComplete
						}
						ei.animateIn();
					}
				}
			}
		} else { // Show
			if (currentVis != "visible" || currentVis === "") {
				this.beforeShowEvent.fire();
				YAHOO.util.Dom.setStyle(this.element, "visibility", "visible");
				this.cfg.refireEvent("iframe");
				this.showEvent.fire();
			}
		}

	} else { // Hide
		if (isMacGecko) {
			this.hideMacGeckoScrollbars();
		}

		if (effect) { // Animate out if showing
			if (currentVis == "visible") {
				this.beforeHideEvent.fire();
				for (var k=0;k<effectInstances.length;k++) {
					var h = effectInstances[k];
					if (k === 0 && ! YAHOO.util.Config.alreadySubscribed(h.animateOutCompleteEvent,this.hideEvent.fire,this.hideEvent)) {
						h.animateOutCompleteEvent.subscribe(this.hideEvent.fire,this.hideEvent,true); // Delegate hideEvent until end of animateOutComplete
					}
					h.animateOut();
				}
			} else if (currentVis === "") {
				YAHOO.util.Dom.setStyle(this.element, "visibility", "hidden");
			}
		} else { // Simple hide
			if (currentVis == "visible" || currentVis === "") {
				this.beforeHideEvent.fire();
				YAHOO.util.Dom.setStyle(this.element, "visibility", "hidden");
				this.cfg.refireEvent("iframe");
				this.hideEvent.fire();
			}
		}
	}
};

/**
* Center event handler used for centering on scroll/resize, but only if the Overlay is visible
* @method doCenterOnDOMEvent
*/
YAHOO.widget.Overlay.prototype.doCenterOnDOMEvent = function() {
	if (this.cfg.getProperty("visible")) {
		this.center();
	}
};

/**
* The default event handler fired when the "fixedcenter" property is changed.
* @method configFixedCenter
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configFixedCenter = function(type, args, obj) {
	var val = args[0];

	if (val) {
		this.center();

		if (! YAHOO.util.Config.alreadySubscribed(this.beforeShowEvent, this.center, this)) {
			this.beforeShowEvent.subscribe(this.center, this, true);
		}

		if (! YAHOO.util.Config.alreadySubscribed(YAHOO.widget.Overlay.windowResizeEvent, this.doCenterOnDOMEvent, this)) {
			YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true);
		}

		if (! YAHOO.util.Config.alreadySubscribed(YAHOO.widget.Overlay.windowScrollEvent, this.doCenterOnDOMEvent, this)) {
			YAHOO.widget.Overlay.windowScrollEvent.subscribe( this.doCenterOnDOMEvent, this, true);
		}
	} else {
		YAHOO.widget.Overlay.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this);
		YAHOO.widget.Overlay.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this);
	}
};

/**
* The default event handler fired when the "height" property is changed.
* @method configHeight
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configHeight = function(type, args, obj) {
	var height = args[0];
	var el = this.element;
	YAHOO.util.Dom.setStyle(el, "height", height);
	this.cfg.refireEvent("iframe");
};

/**
* The default event handler fired when the "width" property is changed.
* @method configWidth
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configWidth = function(type, args, obj) {
	var width = args[0];
	var el = this.element;
	YAHOO.util.Dom.setStyle(el, "width", width);
	this.cfg.refireEvent("iframe");
};

/**
* The default event handler fired when the "zIndex" property is changed.
* @method configzIndex
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configzIndex = function(type, args, obj) {
	var zIndex = args[0];

	var el = this.element;

	if (! zIndex) {
		zIndex = YAHOO.util.Dom.getStyle(el, "zIndex");
		if (! zIndex || isNaN(zIndex)) {
			zIndex = 0;
		}
	}

	if (this.iframe) {
		if (zIndex <= 0) {
			zIndex = 1;
		}
		YAHOO.util.Dom.setStyle(this.iframe, "zIndex", (zIndex-1));
	}

	YAHOO.util.Dom.setStyle(el, "zIndex", zIndex);
	this.cfg.setProperty("zIndex", zIndex, true);
};

/**
* The default event handler fired when the "xy" property is changed.
* @method configXY
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configXY = function(type, args, obj) {
	var pos = args[0];
	var x = pos[0];
	var y = pos[1];

	this.cfg.setProperty("x", x);
	this.cfg.setProperty("y", y);

	this.beforeMoveEvent.fire([x,y]);

	x = this.cfg.getProperty("x");
	y = this.cfg.getProperty("y");

	this.cfg.refireEvent("iframe");
	this.moveEvent.fire([x,y]);
};

/**
* The default event handler fired when the "x" property is changed.
* @method configX
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configX = function(type, args, obj) {
	var x = args[0];
	var y = this.cfg.getProperty("y");

	this.cfg.setProperty("x", x, true);
	this.cfg.setProperty("y", y, true);

	this.beforeMoveEvent.fire([x,y]);

	x = this.cfg.getProperty("x");
	y = this.cfg.getProperty("y");

	YAHOO.util.Dom.setX(this.element, x, true);

	this.cfg.setProperty("xy", [x, y], true);

	this.cfg.refireEvent("iframe");
	this.moveEvent.fire([x, y]);
};

/**
* The default event handler fired when the "y" property is changed.
* @method configY
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configY = function(type, args, obj) {
	var x = this.cfg.getProperty("x");
	var y = args[0];

	this.cfg.setProperty("x", x, true);
	this.cfg.setProperty("y", y, true);

	this.beforeMoveEvent.fire([x,y]);

	x = this.cfg.getProperty("x");
	y = this.cfg.getProperty("y");

	YAHOO.util.Dom.setY(this.element, y, true);

	this.cfg.setProperty("xy", [x, y], true);

	this.cfg.refireEvent("iframe");
	this.moveEvent.fire([x, y]);
};

/**
* Shows the iframe shim, if it has been enabled
* @method showIframe
*/
YAHOO.widget.Overlay.prototype.showIframe = function() {
	if (this.iframe) {
		this.iframe.style.display = "block";
	}
};

/**
* Hides the iframe shim, if it has been enabled
* @method hideIframe
*/
YAHOO.widget.Overlay.prototype.hideIframe = function() {
	if (this.iframe) {
		this.iframe.style.display = "none";
	}
};

/**
* The default event handler fired when the "iframe" property is changed.
* @method configIframe
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configIframe = function(type, args, obj) {

	var val = args[0];

	if (val) { // IFRAME shim is enabled

		if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, this.showIframe, this)) {
			this.showEvent.subscribe(this.showIframe, this, true);
		}
		if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, this.hideIframe, this)) {
			this.hideEvent.subscribe(this.hideIframe, this, true);
		}

		var x = this.cfg.getProperty("x");
		var y = this.cfg.getProperty("y");

		if (! x || ! y) {
			this.syncPosition();
			x = this.cfg.getProperty("x");
			y = this.cfg.getProperty("y");
		}

		if (! isNaN(x) && ! isNaN(y)) {
			if (! this.iframe) {
				this.iframe = document.createElement("iframe");
				if (this.isSecure) {
					this.iframe.src = YAHOO.widget.Overlay.IFRAME_SRC;
				}

				var parent = this.element.parentNode;
				if (parent) {
					parent.appendChild(this.iframe);
				} else {
					document.body.appendChild(this.iframe);
				}

				YAHOO.util.Dom.setStyle(this.iframe, "position", "absolute");
				YAHOO.util.Dom.setStyle(this.iframe, "border", "none");
				YAHOO.util.Dom.setStyle(this.iframe, "margin", "0");
				YAHOO.util.Dom.setStyle(this.iframe, "padding", "0");
				YAHOO.util.Dom.setStyle(this.iframe, "opacity", "0");
				if (this.cfg.getProperty("visible")) {
					this.showIframe();
				} else {
					this.hideIframe();
				}
			}

			var iframeDisplay = YAHOO.util.Dom.getStyle(this.iframe, "display");

			if (iframeDisplay == "none") {
				this.iframe.style.display = "block";
			}

			YAHOO.util.Dom.setXY(this.iframe, [x,y]);

			var width = this.element.clientWidth;
			var height = this.element.clientHeight;

			YAHOO.util.Dom.setStyle(this.iframe, "width", (width+2) + "px");
			YAHOO.util.Dom.setStyle(this.iframe, "height", (height+2) + "px");

			if (iframeDisplay == "none") {
				this.iframe.style.display = "none";
			}
		}
	} else {
		if (this.iframe) {
			this.iframe.style.display = "none";
		}
		this.showEvent.unsubscribe(this.showIframe, this);
		this.hideEvent.unsubscribe(this.hideIframe, this);
	}
};


/**
* The default event handler fired when the "constraintoviewport" property is changed.
* @method configConstrainToViewport
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configConstrainToViewport = function(type, args, obj) {
	var val = args[0];
	if (val) {
		if (! YAHOO.util.Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) {
			this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true);
		}
	} else {
		this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this);
	}
};

/**
* The default event handler fired when the "context" property is changed.
* @method configContext
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.configContext = function(type, args, obj) {
	var contextArgs = args[0];

	if (contextArgs) {
		var contextEl = contextArgs[0];
		var elementMagnetCorner = contextArgs[1];
		var contextMagnetCorner = contextArgs[2];

		if (contextEl) {
			if (typeof contextEl == "string") {
				this.cfg.setProperty("context", [document.getElementById(contextEl),elementMagnetCorner,contextMagnetCorner], true);
			}

			if (elementMagnetCorner && contextMagnetCorner) {
				this.align(elementMagnetCorner, contextMagnetCorner);
			}
		}
	}
};


// END BUILT-IN PROPERTY EVENT HANDLERS //

/**
* Aligns the Overlay to its context element using the specified corner points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, and BOTTOM_RIGHT.
* @method align
* @param {String} elementAlign		The String representing the corner of the Overlay that should be aligned to the context element
* @param {String} contextAlign		The corner of the context element that the elementAlign corner should stick to.
*/
YAHOO.widget.Overlay.prototype.align = function(elementAlign, contextAlign) {
	var contextArgs = this.cfg.getProperty("context");
	if (contextArgs) {
		var context = contextArgs[0];

		var element = this.element;
		var me = this;

		if (! elementAlign) {
			elementAlign = contextArgs[1];
		}

		if (! contextAlign) {
			contextAlign = contextArgs[2];
		}

		if (element && context) {
			var elementRegion = YAHOO.util.Dom.getRegion(element);
			var contextRegion = YAHOO.util.Dom.getRegion(context);

			var doAlign = function(v,h) {
				switch (elementAlign) {
					case YAHOO.widget.Overlay.TOP_LEFT:
						me.moveTo(h,v);
						break;
					case YAHOO.widget.Overlay.TOP_RIGHT:
						me.moveTo(h-element.offsetWidth,v);
						break;
					case YAHOO.widget.Overlay.BOTTOM_LEFT:
						me.moveTo(h,v-element.offsetHeight);
						break;
					case YAHOO.widget.Overlay.BOTTOM_RIGHT:
						me.moveTo(h-element.offsetWidth,v-element.offsetHeight);
						break;
				}
			};

			switch (contextAlign) {
				case YAHOO.widget.Overlay.TOP_LEFT:
					doAlign(contextRegion.top, contextRegion.left);
					break;
				case YAHOO.widget.Overlay.TOP_RIGHT:
					doAlign(contextRegion.top, contextRegion.right);
					break;
				case YAHOO.widget.Overlay.BOTTOM_LEFT:
					doAlign(contextRegion.bottom, contextRegion.left);
					break;
				case YAHOO.widget.Overlay.BOTTOM_RIGHT:
					doAlign(contextRegion.bottom, contextRegion.right);
					break;
			}
		}
	}
};

/**
* The default event handler executed when the moveEvent is fired, if the "constraintoviewport" is set to true.
* @method enforceConstraints
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Overlay.prototype.enforceConstraints = function(type, args, obj) {
	var pos = args[0];

	var x = pos[0];
	var y = pos[1];

	var offsetHeight = this.element.offsetHeight;
	var offsetWidth = this.element.offsetWidth;

	var viewPortWidth = YAHOO.util.Dom.getViewportWidth();
	var viewPortHeight = YAHOO.util.Dom.getViewportHeight();

	var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
	var scrollY = document.documentElement.scrollTop || document.body.scrollTop;

	var topConstraint = scrollY + 10;
	var leftConstraint = scrollX + 10;
	var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
	var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;

	if (x < leftConstraint) {
		x = leftConstraint;
	} else if (x > rightConstraint) {
		x = rightConstraint;
	}

	if (y < topConstraint) {
		y = topConstraint;
	} else if (y > bottomConstraint) {
		y = bottomConstraint;
	}

	this.cfg.setProperty("x", x, true);
	this.cfg.setProperty("y", y, true);
	this.cfg.setProperty("xy", [x,y], true);
};

/**
* Centers the container in the viewport.
* @method center
*/
YAHOO.widget.Overlay.prototype.center = function() {
	var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
	var scrollY = document.documentElement.scrollTop || document.body.scrollTop;

	var viewPortWidth = YAHOO.util.Dom.getClientWidth();
	var viewPortHeight = YAHOO.util.Dom.getClientHeight();

	var elementWidth = this.element.offsetWidth;
	var elementHeight = this.element.offsetHeight;

	var x = (viewPortWidth / 2) - (elementWidth / 2) + scrollX;
	var y = (viewPortHeight / 2) - (elementHeight / 2) + scrollY;

	this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]);

	this.cfg.refireEvent("iframe");
};

/**
* Synchronizes the Panel's "xy", "x", and "y" properties with the Panel's position in the DOM. This is primarily used to update position information during drag & drop.
* @method syncPosition
*/
YAHOO.widget.Overlay.prototype.syncPosition = function() {
	var pos = YAHOO.util.Dom.getXY(this.element);
	this.cfg.setProperty("x", pos[0], true);
	this.cfg.setProperty("y", pos[1], true);
	this.cfg.setProperty("xy", pos, true);
};

/**
* Event handler fired when the resize monitor element is resized.
* @method onDomResize
* @param {DOMEvent} e	The resize DOM event
* @param {Object} obj	The scope object
*/
YAHOO.widget.Overlay.prototype.onDomResize = function(e, obj) {
	YAHOO.widget.Overlay.superclass.onDomResize.call(this, e, obj);
	var me = this;
	setTimeout(function() {
		me.syncPosition();
		me.cfg.refireEvent("iframe");
		me.cfg.refireEvent("context");
	}, 0);
};

/**
* Removes the Overlay element from the DOM and sets all child elements to null.
* @method destroy
*/
YAHOO.widget.Overlay.prototype.destroy = function() {
	if (this.iframe) {
		this.iframe.parentNode.removeChild(this.iframe);
	}

	this.iframe = null;

	YAHOO.widget.Overlay.superclass.destroy.call(this);
};

/**
* Returns a String representation of the object.
* @method toString
* @return {String} The string representation of the Overlay.
*/
YAHOO.widget.Overlay.prototype.toString = function() {
	return "Overlay " + this.id;
};

/**
* A singleton CustomEvent used for reacting to the DOM event for window scroll
* @event YAHOO.widget.Overlay.windowScrollEvent
*/
YAHOO.widget.Overlay.windowScrollEvent = new YAHOO.util.CustomEvent("windowScroll");

/**
* A singleton CustomEvent used for reacting to the DOM event for window resize
* @event YAHOO.widget.Overlay.windowResizeEvent
*/
YAHOO.widget.Overlay.windowResizeEvent = new YAHOO.util.CustomEvent("windowResize");

/**
* The DOM event handler used to fire the CustomEvent for window scroll
* @method YAHOO.widget.Overlay.windowScrollHandler
* @static
* @param {DOMEvent} e The DOM scroll event
*/
YAHOO.widget.Overlay.windowScrollHandler = function(e) {
	if (YAHOO.widget.Module.prototype.browser == "ie" || YAHOO.widget.Module.prototype.browser == "ie7") {
		if (! window.scrollEnd) {
			window.scrollEnd = -1;
		}
		clearTimeout(window.scrollEnd);
		window.scrollEnd = setTimeout(function() { YAHOO.widget.Overlay.windowScrollEvent.fire(); }, 1);
	} else {
		YAHOO.widget.Overlay.windowScrollEvent.fire();
	}
};

/**
* The DOM event handler used to fire the CustomEvent for window resize
* @method YAHOO.widget.Overlay.windowResizeHandler
* @static
* @param {DOMEvent} e The DOM resize event
*/
YAHOO.widget.Overlay.windowResizeHandler = function(e) {
	if (YAHOO.widget.Module.prototype.browser == "ie" || YAHOO.widget.Module.prototype.browser == "ie7") {
		if (! window.resizeEnd) {
			window.resizeEnd = -1;
		}
		clearTimeout(window.resizeEnd);
		window.resizeEnd = setTimeout(function() { YAHOO.widget.Overlay.windowResizeEvent.fire(); }, 100);
	} else {
		YAHOO.widget.Overlay.windowResizeEvent.fire();
	}
};

/**
* A boolean that indicated whether the window resize and scroll events have already been subscribed to.
* @property YAHOO.widget.Overlay._initialized
* @private
* @type Boolean
*/
YAHOO.widget.Overlay._initialized = null;

if (YAHOO.widget.Overlay._initialized === null) {
	YAHOO.util.Event.addListener(window, "scroll", YAHOO.widget.Overlay.windowScrollHandler);
	YAHOO.util.Event.addListener(window, "resize", YAHOO.widget.Overlay.windowResizeHandler);

	YAHOO.widget.Overlay._initialized = true;
}

/**
* OverlayManager is used for maintaining the focus status of multiple Overlays.* @namespace YAHOO.widget
* @namespace YAHOO.widget
* @class OverlayManager
* @constructor
* @param {Array}	overlays	Optional. A collection of Overlays to register with the manager.
* @param {Object}	userConfig		The object literal representing the user configuration of the OverlayManager
*/
YAHOO.widget.OverlayManager = function(userConfig) {
	this.init(userConfig);
};

/**
* The CSS class representing a focused Overlay
* @property YAHOO.widget.OverlayManager.CSS_FOCUSED
* @static
* @final
* @type String
*/
YAHOO.widget.OverlayManager.CSS_FOCUSED = "focused";

YAHOO.widget.OverlayManager.prototype = {
	/**
	* The class's constructor function
	* @property contructor
	* @type Function
	*/
	constructor : YAHOO.widget.OverlayManager,

	/**
	* The array of Overlays that are currently registered
	* @property overlays
	* @type YAHOO.widget.Overlay[]
	*/
	overlays : null,

	/**
	* Initializes the default configuration of the OverlayManager
	* @method initDefaultConfig
	*/
	initDefaultConfig : function() {
		/**
		* The collection of registered Overlays in use by the OverlayManager
		* @config overlays
		* @type YAHOO.widget.Overlay[]
		* @default null
		*/
		this.cfg.addProperty("overlays", { suppressEvent:true } );

		/**
		* The default DOM event that should be used to focus an Overlay
		* @config focusevent
		* @type String
		* @default "mousedown"
		*/
		this.cfg.addProperty("focusevent", { value:"mousedown" } );
	},

	/**
	* Initializes the OverlayManager
	* @method init
	* @param {YAHOO.widget.Overlay[]}	overlays	Optional. A collection of Overlays to register with the manager.
	* @param {Object}	userConfig		The object literal representing the user configuration of the OverlayManager
	*/
	init : function(userConfig) {
		/**
		* The OverlayManager's Config object used for monitoring configuration properties.
		* @property cfg
		* @type YAHOO.util.Config
		*/
		this.cfg = new YAHOO.util.Config(this);

		this.initDefaultConfig();

		if (userConfig) {
			this.cfg.applyConfig(userConfig, true);
		}
		this.cfg.fireQueue();

		/**
		* The currently activated Overlay
		* @property activeOverlay
		* @private
		* @type YAHOO.widget.Overlay
		*/
		var activeOverlay = null;

		/**
		* Returns the currently focused Overlay
		* @method getActive
		* @return {YAHOO.widget.Overlay}	The currently focused Overlay
		*/
		this.getActive = function() {
			return activeOverlay;
		};

		/**
		* Focuses the specified Overlay
		* @method focus
		* @param {YAHOO.widget.Overlay} overlay	The Overlay to focus
		* @param {String} overlay	The id of the Overlay to focus
		*/
		this.focus = function(overlay) {
			var o = this.find(overlay);
			if (o) {
				this.blurAll();
				activeOverlay = o;
				YAHOO.util.Dom.addClass(activeOverlay.element, YAHOO.widget.OverlayManager.CSS_FOCUSED);
				this.overlays.sort(this.compareZIndexDesc);
				var topZIndex = YAHOO.util.Dom.getStyle(this.overlays[0].element, "zIndex");
				if (! isNaN(topZIndex) && this.overlays[0] != overlay) {
					activeOverlay.cfg.setProperty("zIndex", (parseInt(topZIndex, 10) + 2));
				}
				this.overlays.sort(this.compareZIndexDesc);
			}
		};

		/**
		* Removes the specified Overlay from the manager
		* @method remove
		* @param {YAHOO.widget.Overlay}	overlay	The Overlay to remove
		* @param {String} overlay	The id of the Overlay to remove
		*/
		this.remove = function(overlay) {
			var o = this.find(overlay);
			if (o) {
				var originalZ = YAHOO.util.Dom.getStyle(o.element, "zIndex");
				o.cfg.setProperty("zIndex", -1000, true);
				this.overlays.sort(this.compareZIndexDesc);
				this.overlays = this.overlays.slice(0, this.overlays.length-1);
				o.cfg.setProperty("zIndex", originalZ, true);

				o.cfg.setProperty("manager", null);
				o.focusEvent = null;
				o.blurEvent = null;
				o.focus = null;
				o.blur = null;
			}
		};

		/**
		* Removes focus from all registered Overlays in the manager
		* @method blurAll
		*/
		this.blurAll = function() {
			activeOverlay = null;
			for (var o=0;o<this.overlays.length;o++) {
				YAHOO.util.Dom.removeClass(this.overlays[o].element, YAHOO.widget.OverlayManager.CSS_FOCUSED);
			}
		};

		var overlays = this.cfg.getProperty("overlays");

		if (! this.overlays) {
			this.overlays = [];
		}

		if (overlays) {
			this.register(overlays);
			this.overlays.sort(this.compareZIndexDesc);
		}
	},

	/**
	* Registers an Overlay or an array of Overlays with the manager. Upon registration, the Overlay receives functions for focus and blur, along with CustomEvents for each.
	* @method register
	* @param {YAHOO.widget.Overlay}	overlay		An Overlay to register with the manager.
	* @param {YAHOO.widget.Overlay[]}	overlay		An array of Overlays to register with the manager.
	* @return	{Boolean}	True if any Overlays are registered.
	*/
	register : function(overlay) {
		if (overlay instanceof YAHOO.widget.Overlay) {
			overlay.cfg.addProperty("manager", { value:this } );

			overlay.focusEvent = new YAHOO.util.CustomEvent("focus");
			overlay.blurEvent = new YAHOO.util.CustomEvent("blur");

			var mgr=this;

			overlay.focus = function() {
				mgr.focus(this);
				this.focusEvent.fire();
			};

			overlay.blur = function() {
				mgr.blurAll();
				this.blurEvent.fire();
			};

			var focusOnDomEvent = function(e,obj) {
				overlay.focus();
			};

			var focusevent = this.cfg.getProperty("focusevent");
			YAHOO.util.Event.addListener(overlay.element,focusevent,focusOnDomEvent,this,true);

			var zIndex = YAHOO.util.Dom.getStyle(overlay.element, "zIndex");
			if (! isNaN(zIndex)) {
				overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10));
			} else {
				overlay.cfg.setProperty("zIndex", 0);
			}

			this.overlays.push(overlay);
			return true;
		} else if (overlay instanceof Array) {
			var regcount = 0;
			for (var i=0;i<overlay.length;i++) {
				if (this.register(overlay[i])) {
					regcount++;
				}
			}
			if (regcount > 0) {
				return true;
			}
		} else {
			return false;
		}
	},

	/**
	* Attempts to locate an Overlay by instance or ID.
	* @method find
	* @param {YAHOO.widget.Overlay}	overlay		An Overlay to locate within the manager
	* @param {String}	overlay		An Overlay id to locate within the manager
	* @return	{YAHOO.widget.Overlay}	The requested Overlay, if found, or null if it cannot be located.
	*/
	find : function(overlay) {
		if (overlay instanceof YAHOO.widget.Overlay) {
			for (var o=0;o<this.overlays.length;o++) {
				if (this.overlays[o] == overlay) {
					return this.overlays[o];
				}
			}
		} else if (typeof overlay == "string") {
			for (var p=0;p<this.overlays.length;p++) {
				if (this.overlays[p].id == overlay) {
					return this.overlays[p];
				}
			}
		}
		return null;
	},

	/**
	* Used for sorting the manager's Overlays by z-index.
	* @method compareZIndexDesc
	* @private
	* @return {Number}	0, 1, or -1, depending on where the Overlay should fall in the stacking order.
	*/
	compareZIndexDesc : function(o1, o2) {
		var zIndex1 = o1.cfg.getProperty("zIndex");
		var zIndex2 = o2.cfg.getProperty("zIndex");

		if (zIndex1 > zIndex2) {
			return -1;
		} else if (zIndex1 < zIndex2) {
			return 1;
		} else {
			return 0;
		}
	},

	/**
	* Shows all Overlays in the manager.
	* @method showAll
	*/
	showAll : function() {
		for (var o=0;o<this.overlays.length;o++) {
			this.overlays[o].show();
		}
	},

	/**
	* Hides all Overlays in the manager.
	* @method hideAll
	*/
	hideAll : function() {
		for (var o=0;o<this.overlays.length;o++) {
			this.overlays[o].hide();
		}
	},


	/**
	* Returns a string representation of the object.
	* @method toString
	* @return {String}	The string representation of the OverlayManager
	*/
	toString : function() {
		return "OverlayManager";
	}

};

/**
* KeyListener is a utility that provides an easy interface for listening for keydown/keyup events fired against DOM elements.
* @namespace YAHOO.util
* @class KeyListener
* @constructor
* @param {HTMLElement}	attachTo	The element or element ID to which the key event should be attached
* @param {String}	attachTo	The element or element ID to which the key event should be attached
* @param {Object}	keyData		The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
* @param {Function}	handler		The CustomEvent handler to fire when the key event is detected
* @param {Object}	handler		An object literal representing the handler.
* @param {String}	event		Optional. The event (keydown or keyup) to listen for. Defaults automatically to keydown.
*/
YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
	if (! event) {
		event = YAHOO.util.KeyListener.KEYDOWN;
	}

	/**
	* The CustomEvent fired internally when a key is pressed
	* @event keyEvent
	* @private
	* @param {Object}	keyData		The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
	*/
	var keyEvent = new YAHOO.util.CustomEvent("keyPressed");

	/**
	* The CustomEvent fired when the KeyListener is enabled via the enable() function
	* @event enabledEvent
	* @param {Object}	keyData		The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
	*/
	this.enabledEvent = new YAHOO.util.CustomEvent("enabled");

	/**
	* The CustomEvent fired when the KeyListener is disabled via the disable() function
	* @event disabledEvent
	* @param {Object}	keyData		The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
	*/
	this.disabledEvent = new YAHOO.util.CustomEvent("disabled");

	if (typeof attachTo == 'string') {
		attachTo = document.getElementById(attachTo);
	}

	if (typeof handler == 'function') {
		keyEvent.subscribe(handler);
	} else {
		keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
	}

	/**
	* Handles the key event when a key is pressed.
	* @method handleKeyPress
	* @param {DOMEvent} e	The keypress DOM event
	* @param {Object}	obj	The DOM event scope object
	* @private
	*/
	function handleKeyPress(e, obj) {
		if (! keyData.shift) {
			keyData.shift = false;
		}
		if (! keyData.alt) {
			keyData.alt = false;
		}
		if (! keyData.ctrl) {
			keyData.ctrl = false;
		}

		// check held down modifying keys first
		if (e.shiftKey == keyData.shift &&
			e.altKey   == keyData.alt &&
			e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match

			var dataItem;
			var keyPressed;

			if (keyData.keys instanceof Array) {
				for (var i=0;i<keyData.keys.length;i++) {
					dataItem = keyData.keys[i];

					if (dataItem == e.charCode ) {
						keyEvent.fire(e.charCode, e);
						break;
					} else if (dataItem == e.keyCode) {
						keyEvent.fire(e.keyCode, e);
						break;
					}
				}
			} else {
				dataItem = keyData.keys;

				if (dataItem == e.charCode ) {
					keyEvent.fire(e.charCode, e);
				} else if (dataItem == e.keyCode) {
					keyEvent.fire(e.keyCode, e);
				}
			}
		}
	}

	/**
	* Enables the KeyListener by attaching the DOM event listeners to the target DOM element
	* @method enable
	*/
	this.enable = function() {
		if (! this.enabled) {
			YAHOO.util.Event.addListener(attachTo, event, handleKeyPress);
			this.enabledEvent.fire(keyData);
		}
		/**
		* Boolean indicating the enabled/disabled state of the Tooltip
		* @property enabled
		* @type Boolean
		*/
		this.enabled = true;
	};

	/**
	* Disables the KeyListener by removing the DOM event listeners from the target DOM element
	* @method disable
	*/
	this.disable = function() {
		if (this.enabled) {
			YAHOO.util.Event.removeListener(attachTo, event, handleKeyPress);
			this.disabledEvent.fire(keyData);
		}
		this.enabled = false;
	};

	/**
	* Returns a String representation of the object.
	* @method toString
	* @return {String}	The string representation of the KeyListener
	*/
	this.toString = function() {
		return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + (attachTo.id ? "[" + attachTo.id + "]" : "");
	};

};

/**
* Constant representing the DOM "keydown" event.
* @property YAHOO.util.KeyListener.KEYDOWN
* @static
* @final
* @type String
*/
YAHOO.util.KeyListener.KEYDOWN = "keydown";

/**
* Constant representing the DOM "keyup" event.
* @property YAHOO.util.KeyListener.KEYUP
* @static
* @final
* @type String
*/
YAHOO.util.KeyListener.KEYUP = "keyup";

/**
* Tooltip is an implementation of Overlay that behaves like an OS tooltip, displaying when the user mouses over a particular element, and disappearing on mouse out.
* @namespace YAHOO.widget
* @class Tooltip
* @extends YAHOO.widget.Overlay
* @constructor
* @param {String}	el	The element ID representing the Tooltip <em>OR</em>
* @param {HTMLElement}	el	The element representing the Tooltip
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
*/
YAHOO.widget.Tooltip = function(el, userConfig) {
	YAHOO.widget.Tooltip.superclass.constructor.call(this, el, userConfig);
};

YAHOO.extend(YAHOO.widget.Tooltip, YAHOO.widget.Overlay);

/**
* Constant representing the Tooltip CSS class
* @property YAHOO.widget.Tooltip.CSS_TOOLTIP
* @static
* @final
* @type String
*/
YAHOO.widget.Tooltip.CSS_TOOLTIP = "tt";

/**
* The Tooltip initialization method. This method is automatically called by the constructor. A Tooltip is automatically rendered by the init method, and it also is set to be invisible by default, and constrained to viewport by default as well.
* @method init
* @param {String}	el	The element ID representing the Tooltip <em>OR</em>
* @param {HTMLElement}	el	The element representing the Tooltip
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Tooltip. See configuration documentation for more details.
*/
YAHOO.widget.Tooltip.prototype.init = function(el, userConfig) {
	if (document.readyState && document.readyState != "complete") {
		var deferredInit = function() {
			this.init(el, userConfig);
		};
		YAHOO.util.Event.addListener(window, "load", deferredInit, this, true);
	} else {
		YAHOO.widget.Tooltip.superclass.init.call(this, el);

		this.beforeInitEvent.fire(YAHOO.widget.Tooltip);

		YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Tooltip.CSS_TOOLTIP);

		if (userConfig) {
			this.cfg.applyConfig(userConfig, true);
		}

		this.cfg.queueProperty("visible",false);
		this.cfg.queueProperty("constraintoviewport",true);

		this.setBody("");
		this.render(this.cfg.getProperty("container"));

		this.initEvent.fire(YAHOO.widget.Tooltip);
	}
};

/**
* Initializes the class's configurable properties which can be changed using the Overlay's Config object (cfg).
* @method initDefaultConfig
*/
YAHOO.widget.Tooltip.prototype.initDefaultConfig = function() {
	YAHOO.widget.Tooltip.superclass.initDefaultConfig.call(this);

	/**
	* Specifies whether the Tooltip should be kept from overlapping its context element.
	* @config preventoverlap
	* @type Boolean
	* @default true
	*/
	this.cfg.addProperty("preventoverlap",		{ value:true, validator:this.cfg.checkBoolean, supercedes:["x","y","xy"] } );

	/**
	* The number of milliseconds to wait before showing a Tooltip on mouseover.
	* @config showdelay
	* @type Number
	* @default 200
	*/
	this.cfg.addProperty("showdelay",			{ value:200, handler:this.configShowDelay, validator:this.cfg.checkNumber } );

	/**
	* The number of milliseconds to wait before automatically dismissing a Tooltip after the mouse has been resting on the context element.
	* @config autodismissdelay
	* @type Number
	* @default 5000
	*/
	this.cfg.addProperty("autodismissdelay",	{ value:5000, handler:this.configAutoDismissDelay, validator:this.cfg.checkNumber } );

	/**
	* The number of milliseconds to wait before hiding a Tooltip on mouseover.
	* @config hidedelay
	* @type Number
	* @default 250
	*/
	this.cfg.addProperty("hidedelay",			{ value:250, handler:this.configHideDelay, validator:this.cfg.checkNumber } );

	/**
	* Specifies the Tooltip's text.
	* @config text
	* @type String
	* @default null
	*/
	this.cfg.addProperty("text",				{ handler:this.configText, suppressEvent:true } );

	/**
	* Specifies the container element that the Tooltip's markup should be rendered into.
	* @config container
	* @type HTMLElement/String
	* @default document.body
	*/
	this.cfg.addProperty("container",			{ value:document.body, handler:this.configContainer } );

	/**
	* Specifies the element or elements that the Tooltip should be anchored to on mouseover.
	* @config context
	* @type HTMLElement[]/String[]
	* @default null
	*/

};

// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //

/**
* The default event handler fired when the "text" property is changed.
* @method configText
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Tooltip.prototype.configText = function(type, args, obj) {
	var text = args[0];
	if (text) {
		this.setBody(text);
	}
};

/**
* The default event handler fired when the "container" property is changed.
* @method configContainer
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Tooltip.prototype.configContainer = function(type, args, obj) {
	var container = args[0];
	if (typeof container == 'string') {
		this.cfg.setProperty("container", document.getElementById(container), true);
	}
};

/**
* The default event handler fired when the "context" property is changed.
* @method configContext
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Tooltip.prototype.configContext = function(type, args, obj) {
	var context = args[0];
	if (context) {

		// Normalize parameter into an array
		if (! (context instanceof Array)) {
			if (typeof context == "string") {
				this.cfg.setProperty("context", [document.getElementById(context)], true);
			} else { // Assuming this is an element
				this.cfg.setProperty("context", [context], true);
			}
			context = this.cfg.getProperty("context");
		}


		// Remove any existing mouseover/mouseout listeners
		if (this._context) {
			for (var c=0;c<this._context.length;++c) {
				var el = this._context[c];
				YAHOO.util.Event.removeListener(el, "mouseover", this.onContextMouseOver);
				YAHOO.util.Event.removeListener(el, "mousemove", this.onContextMouseMove);
				YAHOO.util.Event.removeListener(el, "mouseout", this.onContextMouseOut);
			}
		}

		// Add mouseover/mouseout listeners to context elements
		this._context = context;
		for (var d=0;d<this._context.length;++d) {
			var el2 = this._context[d];
			YAHOO.util.Event.addListener(el2, "mouseover", this.onContextMouseOver, this);
			YAHOO.util.Event.addListener(el2, "mousemove", this.onContextMouseMove, this);
			YAHOO.util.Event.addListener(el2, "mouseout", this.onContextMouseOut, this);
		}
	}
};

// END BUILT-IN PROPERTY EVENT HANDLERS //

// BEGIN BUILT-IN DOM EVENT HANDLERS //

/**
* The default event handler fired when the user moves the mouse while over the context element.
* @method onContextMouseMove
* @param {DOMEvent} e	The current DOM event
* @param {Object}	obj	The object argument
*/
YAHOO.widget.Tooltip.prototype.onContextMouseMove = function(e, obj) {
	obj.pageX = YAHOO.util.Event.getPageX(e);
	obj.pageY = YAHOO.util.Event.getPageY(e);

};

/**
* The default event handler fired when the user mouses over the context element.
* @method onContextMouseOver
* @param {DOMEvent} e	The current DOM event
* @param {Object}	obj	The object argument
*/
YAHOO.widget.Tooltip.prototype.onContextMouseOver = function(e, obj) {

	if (obj.hideProcId) {
		clearTimeout(obj.hideProcId);
		obj.hideProcId = null;
	}

	var context = this;
	YAHOO.util.Event.addListener(context, "mousemove", obj.onContextMouseMove, obj);

	if (context.title) {
		obj._tempTitle = context.title;
		context.title = "";
	}

	/**
	* The unique process ID associated with the thread responsible for showing the Tooltip.
	* @type int
	*/
	obj.showProcId = obj.doShow(e, context);
};

/**
* The default event handler fired when the user mouses out of the context element.
* @method onContextMouseOut
* @param {DOMEvent} e	The current DOM event
* @param {Object}	obj	The object argument
*/
YAHOO.widget.Tooltip.prototype.onContextMouseOut = function(e, obj) {
	var el = this;

	if (obj._tempTitle) {
		el.title = obj._tempTitle;
		obj._tempTitle = null;
	}

	if (obj.showProcId) {
		clearTimeout(obj.showProcId);
		obj.showProcId = null;
	}

	if (obj.hideProcId) {
		clearTimeout(obj.hideProcId);
		obj.hideProcId = null;
	}


	obj.hideProcId = setTimeout(function() {
				obj.hide();
				}, obj.cfg.getProperty("hidedelay"));
};

// END BUILT-IN DOM EVENT HANDLERS //

/**
* Processes the showing of the Tooltip by setting the timeout delay and offset of the Tooltip.
* @method doShow
* @param {DOMEvent} e	The current DOM event
* @return {Number}	The process ID of the timeout function associated with doShow
*/
YAHOO.widget.Tooltip.prototype.doShow = function(e, context) {

	var yOffset = 25;
	if (this.browser == "opera" && context.tagName == "A") {
		yOffset += 12;
	}

	var me = this;
	return setTimeout(
		function() {
			if (me._tempTitle) {
				me.setBody(me._tempTitle);
			} else {
				me.cfg.refireEvent("text");
			}

			me.moveTo(me.pageX, me.pageY + yOffset);
			if (me.cfg.getProperty("preventoverlap")) {
				me.preventOverlap(me.pageX, me.pageY);
			}

			YAHOO.util.Event.removeListener(context, "mousemove", me.onContextMouseMove);

			me.show();
			me.hideProcId = me.doHide();
		},
	this.cfg.getProperty("showdelay"));
};

/**
* Sets the timeout for the auto-dismiss delay, which by default is 5 seconds, meaning that a tooltip will automatically dismiss itself after 5 seconds of being displayed.
* @method doHide
*/
YAHOO.widget.Tooltip.prototype.doHide = function() {
	var me = this;
	return setTimeout(
		function() {
			me.hide();
		},
		this.cfg.getProperty("autodismissdelay"));
};

/**
* Fired when the Tooltip is moved, this event handler is used to prevent the Tooltip from overlapping with its context element.
* @method preventOverlay
* @param {Number} pageX	The x coordinate position of the mouse pointer
* @param {Number} pageY	The y coordinate position of the mouse pointer
*/
YAHOO.widget.Tooltip.prototype.preventOverlap = function(pageX, pageY) {

	var height = this.element.offsetHeight;

	var elementRegion = YAHOO.util.Dom.getRegion(this.element);

	elementRegion.top -= 5;
	elementRegion.left -= 5;
	elementRegion.right += 5;
	elementRegion.bottom += 5;

	var mousePoint = new YAHOO.util.Point(pageX, pageY);

	if (elementRegion.contains(mousePoint)) {
		this.cfg.setProperty("y", (pageY-height-5));
	}
};

/**
* Returns a string representation of the object.
* @method toString
* @return {String}	The string representation of the Tooltip
*/
YAHOO.widget.Tooltip.prototype.toString = function() {
	return "Tooltip " + this.id;
};

/**
* Panel is an implementation of Overlay that behaves like an OS window, with a draggable header and an optional close icon at the top right.
* @namespace YAHOO.widget
* @class Panel
* @extends YAHOO.widget.Overlay
* @constructor
* @param {String}	el	The element ID representing the Panel <em>OR</em>
* @param {HTMLElement}	el	The element representing the Panel
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Panel. See configuration documentation for more details.
*/
YAHOO.widget.Panel = function(el, userConfig) {
	YAHOO.widget.Panel.superclass.constructor.call(this, el, userConfig);
};

YAHOO.extend(YAHOO.widget.Panel, YAHOO.widget.Overlay);

/**
* Constant representing the default CSS class used for a Panel
* @property YAHOO.widget.Panel.CSS_PANEL
* @static
* @final
* @type String
*/
YAHOO.widget.Panel.CSS_PANEL = "panel";

/**
* Constant representing the default CSS class used for a Panel's wrapping container
* @property YAHOO.widget.Panel.CSS_PANEL_CONTAINER
* @static
* @final
* @type String
*/
YAHOO.widget.Panel.CSS_PANEL_CONTAINER = "panel-container";

/**
* The Overlay initialization method, which is executed for Overlay and all of its subclasses. This method is automatically called by the constructor, and  sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
* @method init
* @param {String}	el	The element ID representing the Overlay <em>OR</em>
* @param {HTMLElement}	el	The element representing the Overlay
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
*/
YAHOO.widget.Panel.prototype.init = function(el, userConfig) {
	YAHOO.widget.Panel.superclass.init.call(this, el/*, userConfig*/);  // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level

	this.beforeInitEvent.fire(YAHOO.widget.Panel);

	YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Panel.CSS_PANEL);

	this.buildWrapper();

	if (userConfig) {
		this.cfg.applyConfig(userConfig, true);
	}

	this.beforeRenderEvent.subscribe(function() {
		var draggable = this.cfg.getProperty("draggable");
		if (draggable) {
			if (! this.header) {
				this.setHeader("&#160;");
			}
		}
	}, this, true);

	var me = this;

    var focusListener = function(ev) {
        var target =  YAHOO.util.Event.getTarget(ev);
        if (! YAHOO.util.Dom.isAncestor(me.element, target)) target.blur();
    };

    this.showMaskEvent.subscribe(function() {

        var checkFocusable = function(el) {
			if (el.tagName == "A" || el.tagName == "BUTTON" || el.tagName == "SELECT" || el.tagName == "INPUT" || el.tagName == "TEXTAREA" || el.tagName == "FORM") {
                if (! YAHOO.util.Dom.isAncestor(me.element, el)) {
                    // Change by avernet. Discussed on:
                    // http://www.nabble.com/Drop-down-%28select%29-in-dialog-tf2852511.html#a7982599
                    // Previous code: YAHOO.util.Event.addListener(el, "focus", el.blur);
                    YAHOO.util.Event.addListener(el, "focus", focusListener);
					return true;
				}
			} else {
				return false;
			}
		};

		this.focusableElements = YAHOO.util.Dom.getElementsBy(checkFocusable);
	}, this, true);

	this.hideMaskEvent.subscribe(function() {
		for (var i=0;i<this.focusableElements.length;i++) {
			var el2 = this.focusableElements[i];
            YAHOO.util.Event.removeListener(el2, "focus", focusListener);
		}
	}, this, true);

	this.beforeShowEvent.subscribe(function() {
		this.cfg.refireEvent("underlay");
	}, this, true);

	this.initEvent.fire(YAHOO.widget.Panel);
};

/**
* Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
*/
YAHOO.widget.Panel.prototype.initEvents = function() {
	YAHOO.widget.Panel.superclass.initEvents.call(this);

	/**
	* CustomEvent fired after the modality mask is shown
	* @event showMaskEvent
	*/
	this.showMaskEvent = new YAHOO.util.CustomEvent("showMask");

	/**
	* CustomEvent fired after the modality mask is hidden
	* @event hideMaskEvent
	*/
	this.hideMaskEvent = new YAHOO.util.CustomEvent("hideMask");

	/**
	* CustomEvent when the Panel is dragged
	* @event dragEvent
	*/
	this.dragEvent = new YAHOO.util.CustomEvent("drag");
};

/**
* Initializes the class's configurable properties which can be changed using the Panel's Config object (cfg).
* @method initDefaultConfig
*/
YAHOO.widget.Panel.prototype.initDefaultConfig = function() {
	YAHOO.widget.Panel.superclass.initDefaultConfig.call(this);

	// Add panel config properties //

	/**
	* True if the Panel should display a "close" button
	* @config close
	* @type Boolean
	* @default true
	*/
	this.cfg.addProperty("close", { value:true, handler:this.configClose, validator:this.cfg.checkBoolean, supercedes:["visible"] } );

	/**
	* True if the Panel should be draggable
	* @config draggable
	* @type Boolean
	* @default true
	*/
	this.cfg.addProperty("draggable", { value:true,	handler:this.configDraggable, validator:this.cfg.checkBoolean, supercedes:["visible"] } );

	/**
	* Sets the type of underlay to display for the Panel. Valid values are "shadow", "matte", and "none".
	* @config underlay
	* @type String
	* @default shadow
	*/
	this.cfg.addProperty("underlay", { value:"shadow", handler:this.configUnderlay, supercedes:["visible"] } );

	/**
	* True if the Panel should be displayed in a modal fashion, automatically creating a transparent mask over the document that will not be removed until the Panel is dismissed.
	* @config modal
	* @type Boolean
	* @default false
	*/
	this.cfg.addProperty("modal",	{ value:false, handler:this.configModal, validator:this.cfg.checkBoolean, supercedes:["visible"] } );

	/**
	* A KeyListener (or array of KeyListeners) that will be enabled when the Panel is shown, and disabled when the Panel is hidden.
	* @config keylisteners
	* @type YAHOO.util.KeyListener[]
	* @default null
	*/
	this.cfg.addProperty("keylisteners", { handler:this.configKeyListeners, suppressEvent:true, supercedes:["visible"] } );
};

// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //

/**
* The default event handler fired when the "close" property is changed. The method controls the appending or hiding of the close icon at the top right of the Panel.
* @method configClose
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configClose = function(type, args, obj) {
	var val = args[0];

	var doHide = function(e, obj) {
		obj.hide();
	};

	if (val) {
		if (! this.close) {
			this.close = document.createElement("DIV");
			YAHOO.util.Dom.addClass(this.close, "close");

			if (this.isSecure) {
				YAHOO.util.Dom.addClass(this.close, "secure");
			} else {
				YAHOO.util.Dom.addClass(this.close, "nonsecure");
			}

			this.close.innerHTML = "&#160;";
			this.innerElement.appendChild(this.close);
			YAHOO.util.Event.addListener(this.close, "click", doHide, this);
		} else {
			this.close.style.display = "block";
		}
	} else {
		if (this.close) {
			this.close.style.display = "none";
		}
	}
};

/**
* The default event handler fired when the "draggable" property is changed.
* @method configDraggable
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configDraggable = function(type, args, obj) {
	var val = args[0];
	if (val) {
		if (this.header) {
			YAHOO.util.Dom.setStyle(this.header,"cursor","move");
			this.registerDragDrop();
		}
	} else {
		if (this.dd) {
			this.dd.unreg();
		}
		if (this.header) {
			YAHOO.util.Dom.setStyle(this.header,"cursor","auto");
		}
	}
};

/**
* The default event handler fired when the "underlay" property is changed.
* @method configUnderlay
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configUnderlay = function(type, args, obj) {
	var val = args[0];

	switch (val.toLowerCase()) {
		case "shadow":
			YAHOO.util.Dom.removeClass(this.element, "matte");
			YAHOO.util.Dom.addClass(this.element, "shadow");

			if (! this.underlay) { // create if not already in DOM
				this.underlay = document.createElement("DIV");
				this.underlay.className = "underlay";
				this.underlay.innerHTML = "&#160;";
				this.element.appendChild(this.underlay);
			}

			this.sizeUnderlay();
			break;
		case "matte":
			YAHOO.util.Dom.removeClass(this.element, "shadow");
			YAHOO.util.Dom.addClass(this.element, "matte");
			break;
		default:
			YAHOO.util.Dom.removeClass(this.element, "shadow");
			YAHOO.util.Dom.removeClass(this.element, "matte");
			break;
	}
};

/**
* The default event handler fired when the "modal" property is changed. This handler subscribes or unsubscribes to the show and hide events to handle the display or hide of the modality mask.
* @method configModal
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configModal = function(type, args, obj) {
	var modal = args[0];

	if (modal) {
		this.buildMask();

		if (! YAHOO.util.Config.alreadySubscribed( this.beforeShowEvent, this.showMask, this ) ) {
			this.beforeShowEvent.subscribe(this.showMask, this, true);
		}
		if (! YAHOO.util.Config.alreadySubscribed( this.hideEvent, this.hideMask, this) ) {
			this.hideEvent.subscribe(this.hideMask, this, true);
		}
		if (! YAHOO.util.Config.alreadySubscribed( YAHOO.widget.Overlay.windowResizeEvent, this.sizeMask, this ) ) {
			YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.sizeMask, this, true);
		}
		if (! YAHOO.util.Config.alreadySubscribed( this.destroyEvent, this.removeMask, this) ) {
			this.destroyEvent.subscribe(this.removeMask, this, true);
		}

		this.cfg.refireEvent("zIndex");
	} else {
		this.beforeShowEvent.unsubscribe(this.showMask, this);
		this.hideEvent.unsubscribe(this.hideMask, this);
		YAHOO.widget.Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this);
		this.destroyEvent.unsubscribe(this.removeMask, this);
	}
};

/**
* Removes the modality mask.
* @method removeMask
*/
YAHOO.widget.Panel.prototype.removeMask = function() {
	if (this.mask) {
		if (this.mask.parentNode) {
			this.mask.parentNode.removeChild(this.mask);
		}
		this.mask = null;
	}
};

/**
* The default event handler fired when the "keylisteners" property is changed.
* @method configKeyListeners
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configKeyListeners = function(type, args, obj) {
	var listeners = args[0];

	if (listeners) {
		if (listeners instanceof Array) {
			for (var i=0;i<listeners.length;i++) {
				var listener = listeners[i];

				if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, listener.enable, listener)) {
					this.showEvent.subscribe(listener.enable, listener, true);
				}
				if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, listener.disable, listener)) {
					this.hideEvent.subscribe(listener.disable, listener, true);
					this.destroyEvent.subscribe(listener.disable, listener, true);
				}
			}
		} else {
			if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, listeners.enable, listeners)) {
				this.showEvent.subscribe(listeners.enable, listeners, true);
			}
			if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, listeners.disable, listeners)) {
				this.hideEvent.subscribe(listeners.disable, listeners, true);
				this.destroyEvent.subscribe(listeners.disable, listeners, true);
			}
		}
	}
};

/**
* The default event handler fired when the "height" property is changed.
* @method configHeight
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configHeight = function(type, args, obj) {
	var height = args[0];
	var el = this.innerElement;
	YAHOO.util.Dom.setStyle(el, "height", height);
	this.cfg.refireEvent("underlay");
	this.cfg.refireEvent("iframe");
};

/**
* The default event handler fired when the "width" property is changed.
* @method configWidth
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configWidth = function(type, args, obj) {
	var width = args[0];
	var el = this.innerElement;
	YAHOO.util.Dom.setStyle(el, "width", width);
	this.cfg.refireEvent("underlay");
	this.cfg.refireEvent("iframe");
};

/**
* The default event handler fired when the "zIndex" property is changed.
* @method configzIndex
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Panel.prototype.configzIndex = function(type, args, obj) {
	YAHOO.widget.Panel.superclass.configzIndex.call(this, type, args, obj);

	var maskZ = 0;
	var currentZ = YAHOO.util.Dom.getStyle(this.element, "zIndex");

	if (this.mask) {
		if (! currentZ || isNaN(currentZ)) {
			currentZ = 0;
		}

		if (currentZ === 0) {
			this.cfg.setProperty("zIndex", 1);
		} else {
			maskZ = currentZ - 1;
			YAHOO.util.Dom.setStyle(this.mask, "zIndex", maskZ);
		}

	}
};

// END BUILT-IN PROPERTY EVENT HANDLERS //

/**
* Builds the wrapping container around the Panel that is used for positioning the shadow and matte underlays. The container element is assigned to a  local instance variable called container, and the element is reinserted inside of it.
* @method buildWrapper
*/
YAHOO.widget.Panel.prototype.buildWrapper = function() {
	var elementParent = this.element.parentNode;
	var originalElement = this.element;

	var wrapper = document.createElement("DIV");
	wrapper.className = YAHOO.widget.Panel.CSS_PANEL_CONTAINER;
	wrapper.id = originalElement.id + "_c";

	if (elementParent) {
		elementParent.insertBefore(wrapper, originalElement);
	}

	wrapper.appendChild(originalElement);

	this.element = wrapper;
	this.innerElement = originalElement;

	YAHOO.util.Dom.setStyle(this.innerElement, "visibility", "inherit");
};

/**
* Adjusts the size of the shadow based on the size of the element.
* @method sizeUnderlay
*/
YAHOO.widget.Panel.prototype.sizeUnderlay = function() {
	if (this.underlay && this.browser != "gecko" && this.browser != "safari") {
		this.underlay.style.width = this.innerElement.offsetWidth + "px";
		this.underlay.style.height = this.innerElement.offsetHeight + "px";
	}
};

/**
* Event handler fired when the resize monitor element is resized.
* @method onDomResize
* @param {DOMEvent} e	The resize DOM event
* @param {Object} obj	The scope object
*/
YAHOO.widget.Panel.prototype.onDomResize = function(e, obj) {
	YAHOO.widget.Panel.superclass.onDomResize.call(this, e, obj);
	var me = this;
	setTimeout(function() {
		me.sizeUnderlay();
	}, 0);
};

/**
* Registers the Panel's header for drag & drop capability.
* @method registerDragDrop
*/
YAHOO.widget.Panel.prototype.registerDragDrop = function() {
	if (this.header) {
		this.dd = new YAHOO.util.DD(this.element.id, this.id);

		if (! this.header.id) {
			this.header.id = this.id + "_h";
		}

		var me = this;

		this.dd.startDrag = function() {

			if (me.browser == "ie") {
				YAHOO.util.Dom.addClass(me.element,"drag");
			}

			if (me.cfg.getProperty("constraintoviewport")) {
				var offsetHeight = me.element.offsetHeight;
				var offsetWidth = me.element.offsetWidth;

				var viewPortWidth = YAHOO.util.Dom.getViewportWidth();
				var viewPortHeight = YAHOO.util.Dom.getViewportHeight();

				var scrollX = window.scrollX || document.documentElement.scrollLeft;
				var scrollY = window.scrollY || document.documentElement.scrollTop;

				var topConstraint = scrollY + 10;
				var leftConstraint = scrollX + 10;
				var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
				var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;

				this.minX = leftConstraint;
				this.maxX = rightConstraint;
				this.constrainX = true;

				this.minY = topConstraint;
				this.maxY = bottomConstraint;
				this.constrainY = true;
			} else {
				this.constrainX = false;
				this.constrainY = false;
			}

			me.dragEvent.fire("startDrag", arguments);
		};

		this.dd.onDrag = function() {
			me.syncPosition();
			me.cfg.refireEvent("iframe");
			if (this.platform == "mac" && this.browser == "gecko") {
				this.showMacGeckoScrollbars();
			}

			me.dragEvent.fire("onDrag", arguments);
		};

		this.dd.endDrag = function() {
			if (me.browser == "ie") {
				YAHOO.util.Dom.removeClass(me.element,"drag");
			}

			me.dragEvent.fire("endDrag", arguments);
		};

		this.dd.setHandleElId(this.header.id);
		this.dd.addInvalidHandleType("INPUT");
		this.dd.addInvalidHandleType("SELECT");
		this.dd.addInvalidHandleType("TEXTAREA");
	}
};

/**
* Builds the mask that is laid over the document when the Panel is configured to be modal.
* @method buildMask
*/
YAHOO.widget.Panel.prototype.buildMask = function() {
	if (! this.mask) {
		this.mask = document.createElement("DIV");
		this.mask.id = this.id + "_mask";
		this.mask.className = "mask";
		this.mask.innerHTML = "&#160;";

		var maskClick = function(e, obj) {
			YAHOO.util.Event.stopEvent(e);
		};

		var firstChild = document.body.firstChild;
		if (firstChild)	{
			document.body.insertBefore(this.mask, document.body.firstChild);
		} else {
			document.body.appendChild(this.mask);
		}
	}
};

/**
* Hides the modality mask.
* @method hideMask
*/
YAHOO.widget.Panel.prototype.hideMask = function() {
	if (this.cfg.getProperty("modal") && this.mask) {
		this.mask.style.display = "none";
		this.hideMaskEvent.fire();
		YAHOO.util.Dom.removeClass(document.body, "masked");
	}
};

/**
* Shows the modality mask.
* @method showMask
*/
YAHOO.widget.Panel.prototype.showMask = function() {
	if (this.cfg.getProperty("modal") && this.mask) {
		YAHOO.util.Dom.addClass(document.body, "masked");
		this.sizeMask();
		this.mask.style.display = "block";
		this.showMaskEvent.fire();
	}
};

/**
* Sets the size of the modality mask to cover the entire scrollable area of the document
* @method sizeMask
*/
YAHOO.widget.Panel.prototype.sizeMask = function() {
	if (this.mask) {
		this.mask.style.height = YAHOO.util.Dom.getDocumentHeight()+"px";
		this.mask.style.width = YAHOO.util.Dom.getDocumentWidth()+"px";
	}
};

/**
* Renders the Panel by inserting the elements that are not already in the main Panel into their correct places. Optionally appends the Panel to the specified node prior to the render's execution. NOTE: For Panels without existing markup, the appendToNode argument is REQUIRED. If this argument is ommitted and the current element is not present in the document, the function will return false, indicating that the render was a failure.
* @method render
* @param {String}	appendToNode	The element id to which the Module should be appended to prior to rendering <em>OR</em>
* @param {HTMLElement}	appendToNode	The element to which the Module should be appended to prior to rendering
* @return {boolean} Success or failure of the render
*/
YAHOO.widget.Panel.prototype.render = function(appendToNode) {
	return YAHOO.widget.Panel.superclass.render.call(this, appendToNode, this.innerElement);
};

/**
* Returns a String representation of the object.
* @method toString
* @return {String} The string representation of the Panel.
*/
YAHOO.widget.Panel.prototype.toString = function() {
	return "Panel " + this.id;
};

/**
* Dialog is an implementation of Panel that can be used to submit form data. Built-in functionality for buttons with event handlers is included, and button sets can be build dynamically, or the preincluded ones for Submit/Cancel and OK/Cancel can be utilized. Forms can be processed in 3 ways -- via an asynchronous Connection utility call, a simple form POST or GET, or manually.
* @namespace YAHOO.widget
* @class Dialog
* @extends YAHOO.widget.Panel
* @constructor
* @param {String}	el	The element ID representing the Dialog <em>OR</em>
* @param {HTMLElement}	el	The element representing the Dialog
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Dialog. See configuration documentation for more details.
*/
YAHOO.widget.Dialog = function(el, userConfig) {
	YAHOO.widget.Dialog.superclass.constructor.call(this, el, userConfig);
};

YAHOO.extend(YAHOO.widget.Dialog, YAHOO.widget.Panel);

/**
* Constant representing the default CSS class used for a Dialog
* @property YAHOO.widget.Dialog.CSS_DIALOG
* @static
* @final
* @type String
*/
YAHOO.widget.Dialog.CSS_DIALOG = "dialog";

/**
* Initializes the class's configurable properties which can be changed using the Dialog's Config object (cfg).
* @method initDefaultConfig
*/
YAHOO.widget.Dialog.prototype.initDefaultConfig = function() {
	YAHOO.widget.Dialog.superclass.initDefaultConfig.call(this);

	/**
	* The internally maintained callback object for use with the Connection utility
	* @property callback
	* @type Object
	*/
	this.callback = {
		/**
		* The function to execute upon success of the Connection submission
		* @property callback.success
		* @type Function
		*/
		success : null,
		/**
		* The function to execute upon failure of the Connection submission
		* @property callback.failure
		* @type Function
		*/
		failure : null,
		/**
		* The arbitraty argument or arguments to pass to the Connection callback functions
		* @property callback.argument
		* @type Object
		*/
		argument: null
	};

	// Add form dialog config properties //

	/**
	* The method to use for posting the Dialog's form. Possible values are "async", "form", and "manual".
	* @config postmethod
	* @type String
	* @default async
	*/
	this.cfg.addProperty("postmethod", { value:"async", validator:function(val) {
													if (val != "form" && val != "async" && val != "none" && val != "manual") {
														return false;
													} else {
														return true;
													}
												} });

	/**
	* Object literal(s) defining the buttons for the Dialog's footer.
	* @config buttons
	* @type Object[]
	* @default "none"
	*/
	this.cfg.addProperty("buttons",		{ value:"none",	handler:this.configButtons } );
};

/**
* Initializes the custom events for Dialog which are fired automatically at appropriate times by the Dialog class.
* @method initEvents
*/
YAHOO.widget.Dialog.prototype.initEvents = function() {
	YAHOO.widget.Dialog.superclass.initEvents.call(this);

	/**
	* CustomEvent fired prior to submission
	* @event beforeSumitEvent
	*/
	this.beforeSubmitEvent	= new YAHOO.util.CustomEvent("beforeSubmit");

	/**
	* CustomEvent fired after submission
	* @event submitEvent
	*/
	this.submitEvent		= new YAHOO.util.CustomEvent("submit");

	/**
	* CustomEvent fired prior to manual submission
	* @event manualSubmitEvent
	*/
	this.manualSubmitEvent	= new YAHOO.util.CustomEvent("manualSubmit");

	/**
	* CustomEvent fired prior to asynchronous submission
	* @event asyncSubmitEvent
	*/
	this.asyncSubmitEvent	= new YAHOO.util.CustomEvent("asyncSubmit");

	/**
	* CustomEvent fired prior to form-based submission
	* @event formSubmitEvent
	*/
	this.formSubmitEvent	= new YAHOO.util.CustomEvent("formSubmit");

	/**
	* CustomEvent fired after cancel
	* @event cancelEvent
	*/
	this.cancelEvent		= new YAHOO.util.CustomEvent("cancel");
};

/**
* The Dialog initialization method, which is executed for Dialog and all of its subclasses. This method is automatically called by the constructor, and  sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
* @method init
* @param {String}	el	The element ID representing the Dialog <em>OR</em>
* @param {HTMLElement}	el	The element representing the Dialog
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this Dialog. See configuration documentation for more details.
*/
YAHOO.widget.Dialog.prototype.init = function(el, userConfig) {
	YAHOO.widget.Dialog.superclass.init.call(this, el/*, userConfig*/);  // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level

	this.beforeInitEvent.fire(YAHOO.widget.Dialog);

	YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Dialog.CSS_DIALOG);

	this.cfg.setProperty("visible", false);

	if (userConfig) {
		this.cfg.applyConfig(userConfig, true);
	}

	this.renderEvent.subscribe(this.registerForm, this, true);

	this.showEvent.subscribe(this.focusFirst, this, true);
	this.beforeHideEvent.subscribe(this.blurButtons, this, true);

	this.beforeRenderEvent.subscribe(function() {
		var buttonCfg = this.cfg.getProperty("buttons");
		if (buttonCfg && buttonCfg != "none") {
			if (! this.footer) {
				this.setFooter("");
			}
		}
	}, this, true);

	this.initEvent.fire(YAHOO.widget.Dialog);
};

/**
* Performs the submission of the Dialog form depending on the value of "postmethod" property.
* @method doSubmit
*/
YAHOO.widget.Dialog.prototype.doSubmit = function() {
	var pm = this.cfg.getProperty("postmethod");
	switch (pm) {
		case "async":
			var method = this.form.getAttribute("method") || 'POST';
			method = method.toUpperCase();
			YAHOO.util.Connect.setForm(this.form);
			var cObj = YAHOO.util.Connect.asyncRequest(method, this.form.getAttribute("action"), this.callback);
			this.asyncSubmitEvent.fire();
			break;
		case "form":
			this.form.submit();
			this.formSubmitEvent.fire();
			break;
		case "none":
		case "manual":
			this.manualSubmitEvent.fire();
			break;
	}
};

/**
* Prepares the Dialog's internal FORM object, creating one if one is not currently present.
* @method registerForm
*/
YAHOO.widget.Dialog.prototype.registerForm = function() {
    return;
    var form = this.element.getElementsByTagName("FORM")[0];

	if (! form) {
		var formHTML = "<form name=\"frm_" + this.id + "\" action=\"\"></form>";
		this.body.innerHTML += formHTML;
		form = this.element.getElementsByTagName("FORM")[0];
	}

	this.firstFormElement = function() {
		for (var f=0;f<form.elements.length;f++ ) {
			var el = form.elements[f];
			if (el.focus) {
				if (el.type && el.type != "hidden") {
					return el;
				}
			}
		}
		return null;
	}();

	this.lastFormElement = function() {
		for (var f=form.elements.length-1;f>=0;f-- ) {
			var el = form.elements[f];
			if (el.focus) {
				if (el.type && el.type != "hidden") {
					return el;
				}
			}
		}
		return null;
	}();

	this.form = form;

	if (this.cfg.getProperty("modal") && this.form) {

		var me = this;

		var firstElement = this.firstFormElement || this.firstButton;
		if (firstElement) {
			this.preventBackTab = new YAHOO.util.KeyListener(firstElement, { shift:true, keys:9 }, {fn:me.focusLast, scope:me, correctScope:true} );
			this.showEvent.subscribe(this.preventBackTab.enable, this.preventBackTab, true);
			this.hideEvent.subscribe(this.preventBackTab.disable, this.preventBackTab, true);
		}

		var lastElement = this.lastButton || this.lastFormElement;
		if (lastElement) {
			this.preventTabOut = new YAHOO.util.KeyListener(lastElement, { shift:false, keys:9 }, {fn:me.focusFirst, scope:me, correctScope:true} );
			this.showEvent.subscribe(this.preventTabOut.enable, this.preventTabOut, true);
			this.hideEvent.subscribe(this.preventTabOut.disable, this.preventTabOut, true);
		}
	}
};

// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //

/**
* The default event handler for the "buttons" configuration property
* @method configButtons
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.Dialog.prototype.configButtons = function(type, args, obj) {
	var buttons = args[0];
	if (buttons != "none") {
		this.buttonSpan = null;
		this.buttonSpan = document.createElement("SPAN");
		this.buttonSpan.className = "button-group";

		for (var b=0;b<buttons.length;b++) {
			var button = buttons[b];

			var htmlButton = document.createElement("BUTTON");
			htmlButton.setAttribute("type", "button");

			if (button.isDefault) {
				htmlButton.className = "default";
				this.defaultHtmlButton = htmlButton;
			}

			htmlButton.appendChild(document.createTextNode(button.text));
			YAHOO.util.Event.addListener(htmlButton, "click", button.handler, this, true);

			this.buttonSpan.appendChild(htmlButton);
			button.htmlButton = htmlButton;

			if (b === 0) {
				this.firstButton = button.htmlButton;
			}

			if (b == (buttons.length-1)) {
				this.lastButton = button.htmlButton;
			}

		}

		this.setFooter(this.buttonSpan);

		this.cfg.refireEvent("iframe");
		this.cfg.refireEvent("underlay");
	} else { // Do cleanup
		if (this.buttonSpan) {
			if (this.buttonSpan.parentNode) {
				this.buttonSpan.parentNode.removeChild(this.buttonSpan);
			}

			this.buttonSpan = null;
			this.firstButton = null;
			this.lastButton = null;
			this.defaultHtmlButton = null;
		}
	}
};


/**
* The default event handler used to focus the first field of the form when the Dialog is shown.
* @method focusFirst
*/
YAHOO.widget.Dialog.prototype.focusFirst = function(type,args,obj) {
	if (args) {
		var e = args[1];
		if (e) {
			YAHOO.util.Event.stopEvent(e);
		}
	}

	if (this.firstFormElement) {
		this.firstFormElement.focus();
	} else {
		this.focusDefaultButton();
	}
};

/**
* Sets the focus to the last button in the button or form element in the Dialog
* @method focusLast
*/
YAHOO.widget.Dialog.prototype.focusLast = function(type,args,obj) {
	if (args) {
		var e = args[1];
		if (e) {
			YAHOO.util.Event.stopEvent(e);
		}
	}

	var buttons = this.cfg.getProperty("buttons");
	if (buttons && buttons instanceof Array) {
		this.focusLastButton();
	} else {
		if (this.lastFormElement) {
			this.lastFormElement.focus();
		}
	}
};

/**
* Sets the focus to the button that is designated as the default. By default, his handler is executed when the show event is fired.
* @method focusDefaultButton
*/
YAHOO.widget.Dialog.prototype.focusDefaultButton = function() {
	if (this.defaultHtmlButton) {
		this.defaultHtmlButton.focus();
	}
};

/**
* Blurs all the html buttons
* @method blurButtons
*/
YAHOO.widget.Dialog.prototype.blurButtons = function() {
	var buttons = this.cfg.getProperty("buttons");
	if (buttons && buttons instanceof Array) {
		var html = buttons[0].htmlButton;
		if (html) {
			html.blur();
		}
	}
};

/**
* Sets the focus to the first button in the button list
* @method focusFirstButton
*/
YAHOO.widget.Dialog.prototype.focusFirstButton = function() {
	var buttons = this.cfg.getProperty("buttons");
	if (buttons && buttons instanceof Array) {
		var html = buttons[0].htmlButton;
		if (html) {
			html.focus();
		}
	}
};

/**
* Sets the focus to the first button in the button list
* @method focusLastButton
*/
YAHOO.widget.Dialog.prototype.focusLastButton = function() {
	var buttons = this.cfg.getProperty("buttons");
	if (buttons && buttons instanceof Array) {
		var html = buttons[buttons.length-1].htmlButton;
		if (html) {
			html.focus();
		}
	}
};

// END BUILT-IN PROPERTY EVENT HANDLERS //

/**
* Built-in function hook for writing a validation function that will be checked for a "true" value prior to a submit. This function, as implemented by default, always returns true, so it should be overridden if validation is necessary.
* @method validate
*/
YAHOO.widget.Dialog.prototype.validate = function() {
	return true;
};

/**
* Executes a submit of the Dialog followed by a hide, if validation is successful.
* @method submit
*/
YAHOO.widget.Dialog.prototype.submit = function() {
	if (this.validate()) {
		this.beforeSubmitEvent.fire();
		this.doSubmit();
		this.submitEvent.fire();
		this.hide();
		return true;
	} else {
		return false;
	}
};

/**
* Executes the cancel of the Dialog followed by a hide.
* @method cancel
*/
YAHOO.widget.Dialog.prototype.cancel = function() {
	this.cancelEvent.fire();
	this.hide();
};

/**
* Returns a JSON-compatible data structure representing the data currently contained in the form.
* @method getData
* @return {Object} A JSON object reprsenting the data of the current form.
*/
YAHOO.widget.Dialog.prototype.getData = function() {
	var form = this.form;
	var data = {};

	if (form) {
		for (var i in this.form) {
			var formItem = form[i];
			if (formItem) {
				if (formItem.tagName) { // Got a single form item
					switch (formItem.tagName) {
						case "INPUT":
							switch (formItem.type) {
								case "checkbox":
									data[i] = formItem.checked;
									break;
								case "textbox":
								case "text":
								case "hidden":
									data[i] = formItem.value;
									break;
							}
							break;
						case "TEXTAREA":
							data[i] = formItem.value;
							break;
						case "SELECT":
							var val = [];
							for (var x=0;x<formItem.options.length;x++)	{
								var option = formItem.options[x];
								if (option.selected) {
									var selval = option.value;
									if (! selval || selval === "") {
										selval = option.text;
									}
									val[val.length] = selval;
								}
							}
							data[i] = val;
							break;
					}
				} else if (formItem[0] && formItem[0].tagName) { // this is an array of form items
					if (formItem[0].tagName == "INPUT") {
						switch (formItem[0].type) {
							case "radio":
								for (var r=0; r<formItem.length; r++) {
									var radio = formItem[r];
									if (radio.checked) {
										data[radio.name] = radio.value;
										break;
									}
								}
								break;
							case "checkbox":
								var cbArray = [];
								for (var c=0; c<formItem.length; c++) {
									var check = formItem[c];
									if (check.checked) {
										cbArray[cbArray.length] = check.value;
									}
								}
								data[formItem[0].name] = cbArray;
								break;
						}
					}
				}
			}
		}
	}
	return data;
};

/**
* Returns a string representation of the object.
* @method toString
* @return {String}	The string representation of the Dialog
*/
YAHOO.widget.Dialog.prototype.toString = function() {
	return "Dialog " + this.id;
};

/**
* SimpleDialog is a simple implementation of Dialog that can be used to submit a single value. Forms can be processed in 3 ways -- via an asynchronous Connection utility call, a simple form POST or GET, or manually.
* @namespace YAHOO.widget
* @class SimpleDialog
* @extends YAHOO.widget.Dialog
* @constructor
* @param {String}	el	The element ID representing the SimpleDialog <em>OR</em>
* @param {HTMLElement}	el	The element representing the SimpleDialog
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this SimpleDialog. See configuration documentation for more details.
*/
YAHOO.widget.SimpleDialog = function(el, userConfig) {
	YAHOO.widget.SimpleDialog.superclass.constructor.call(this, el, userConfig);
};

YAHOO.extend(YAHOO.widget.SimpleDialog, YAHOO.widget.Dialog);

/**
* Constant for the standard network icon for a blocking action
* @property YAHOO.widget.SimpleDialog.ICON_BLOCK
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_BLOCK = "nt/ic/ut/bsc/blck16_1.gif";

/**
* Constant for the standard network icon for alarm
* @property YAHOO.widget.SimpleDialog.ICON_ALARM
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_ALARM = "nt/ic/ut/bsc/alrt16_1.gif";

/**
* Constant for the standard network icon for help
* @property YAHOO.widget.SimpleDialog.ICON_HELP
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_HELP  = "nt/ic/ut/bsc/hlp16_1.gif";

/**
* Constant for the standard network icon for info
* @property YAHOO.widget.SimpleDialog.ICON_INFO
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_INFO  = "nt/ic/ut/bsc/info16_1.gif";

/**
* Constant for the standard network icon for warn
* @property YAHOO.widget.SimpleDialog.ICON_WARN
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_WARN  = "nt/ic/ut/bsc/warn16_1.gif";

/**
* Constant for the standard network icon for a tip
* @property YAHOO.widget.SimpleDialog.ICON_TIP
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.ICON_TIP   = "nt/ic/ut/bsc/tip16_1.gif";

/**
* Constant representing the default CSS class used for a SimpleDialog
* @property YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG
* @static
* @final
* @type String
*/
YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG = "simple-dialog";

/**
* Initializes the class's configurable properties which can be changed using the SimpleDialog's Config object (cfg).
* @method initDefaultConfig
*/
YAHOO.widget.SimpleDialog.prototype.initDefaultConfig = function() {
	YAHOO.widget.SimpleDialog.superclass.initDefaultConfig.call(this);

	// Add dialog config properties //

	/**
	* Sets the informational icon for the SimpleDialog
	* @config icon
	* @type String
	* @default "none"
	*/
	this.cfg.addProperty("icon",	{ value:"none",	handler:this.configIcon, suppressEvent:true } );

	/**
	* Sets the text for the SimpleDialog
	* @config text
	* @type String
	* @default ""
	*/
	this.cfg.addProperty("text",	{ value:"", handler:this.configText, suppressEvent:true, supercedes:["icon"] } );
};


/**
* The SimpleDialog initialization method, which is executed for SimpleDialog and all of its subclasses. This method is automatically called by the constructor, and  sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
* @method init
* @param {String}	el	The element ID representing the SimpleDialog <em>OR</em>
* @param {HTMLElement}	el	The element representing the SimpleDialog
* @param {Object}	userConfig	The configuration object literal containing the configuration that should be set for this SimpleDialog. See configuration documentation for more details.
*/
YAHOO.widget.SimpleDialog.prototype.init = function(el, userConfig) {
	YAHOO.widget.SimpleDialog.superclass.init.call(this, el/*, userConfig*/);  // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level

	this.beforeInitEvent.fire(YAHOO.widget.SimpleDialog);

	YAHOO.util.Dom.addClass(this.element, YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG);

	this.cfg.queueProperty("postmethod", "manual");

	if (userConfig) {
		this.cfg.applyConfig(userConfig, true);
	}

	this.beforeRenderEvent.subscribe(function() {
		if (! this.body) {
			this.setBody("");
		}
	}, this, true);

	this.initEvent.fire(YAHOO.widget.SimpleDialog);

};
/**
* Prepares the SimpleDialog's internal FORM object, creating one if one is not currently present, and adding the value hidden field.
* @method registerForm
*/
YAHOO.widget.SimpleDialog.prototype.registerForm = function() {
	YAHOO.widget.SimpleDialog.superclass.registerForm.call(this);
	this.form.innerHTML += "<input type=\"hidden\" name=\"" + this.id + "\" value=\"\"/>";
};

// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //

/**
* Fired when the "icon" property is set.
* @method configIcon
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.SimpleDialog.prototype.configIcon = function(type,args,obj) {
	var icon = args[0];
	if (icon && icon != "none") {
		var iconHTML = "<img src=\"" + this.imageRoot + icon + "\" class=\"icon\" />";
		this.body.innerHTML = iconHTML + this.body.innerHTML;
	}
};

/**
* Fired when the "text" property is set.
* @method configText
* @param {String} type	The CustomEvent type (usually the property name)
* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
*/
YAHOO.widget.SimpleDialog.prototype.configText = function(type,args,obj) {
	var text = args[0];
	if (text) {
		this.setBody(text);
		this.cfg.refireEvent("icon");
	}
};
// END BUILT-IN PROPERTY EVENT HANDLERS //

/**
* Returns a string representation of the object.
* @method toString
* @return {String}	The string representation of the SimpleDialog
*/
YAHOO.widget.SimpleDialog.prototype.toString = function() {
	return "SimpleDialog " + this.id;
};

/**
* ContainerEffect encapsulates animation transitions that are executed when an Overlay is shown or hidden.
* @namespace YAHOO.widget
* @class ContainerEffect
* @constructor
* @param {YAHOO.widget.Overlay}	overlay		The Overlay that the animation should be associated with
* @param {Object}	attrIn		The object literal representing the animation arguments to be used for the animate-in transition. The arguments for this literal are: attributes(object, see YAHOO.util.Anim for description), duration(Number), and method(i.e. YAHOO.util.Easing.easeIn).
* @param {Object}	attrOut		The object literal representing the animation arguments to be used for the animate-out transition. The arguments for this literal are: attributes(object, see YAHOO.util.Anim for description), duration(Number), and method(i.e. YAHOO.util.Easing.easeIn).
* @param {HTMLElement}	targetElement	Optional. The target element that should be animated during the transition. Defaults to overlay.element.
* @param {class}	Optional. The animation class to instantiate. Defaults to YAHOO.util.Anim. Other options include YAHOO.util.Motion.
*/
YAHOO.widget.ContainerEffect = function(overlay, attrIn, attrOut, targetElement, animClass) {
	if (! animClass) {
		animClass = YAHOO.util.Anim;
	}

	/**
	* The overlay to animate
	* @property overlay
	* @type YAHOO.widget.Overlay
	*/
	this.overlay = overlay;
	/**
	* The animation attributes to use when transitioning into view
	* @property attrIn
	* @type Object
	*/
	this.attrIn = attrIn;
	/**
	* The animation attributes to use when transitioning out of view
	* @property attrOut
	* @type Object
	*/
	this.attrOut = attrOut;
	/**
	* The target element to be animated
	* @property targetElement
	* @type HTMLElement
	*/
	this.targetElement = targetElement || overlay.element;
	/**
	* The animation class to use for animating the overlay
	* @property animClass
	* @type class
	*/
	this.animClass = animClass;
};

/**
* Initializes the animation classes and events.
* @method init
*/
YAHOO.widget.ContainerEffect.prototype.init = function() {
	this.beforeAnimateInEvent = new YAHOO.util.CustomEvent("beforeAnimateIn");
	this.beforeAnimateOutEvent = new YAHOO.util.CustomEvent("beforeAnimateOut");

	this.animateInCompleteEvent = new YAHOO.util.CustomEvent("animateInComplete");
	this.animateOutCompleteEvent = new YAHOO.util.CustomEvent("animateOutComplete");

	this.animIn = new this.animClass(this.targetElement, this.attrIn.attributes, this.attrIn.duration, this.attrIn.method);
	this.animIn.onStart.subscribe(this.handleStartAnimateIn, this);
	this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this);
	this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn, this);

	this.animOut = new this.animClass(this.targetElement, this.attrOut.attributes, this.attrOut.duration, this.attrOut.method);
	this.animOut.onStart.subscribe(this.handleStartAnimateOut, this);
	this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this);
	this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this);
};

/**
* Triggers the in-animation.
* @method animateIn
*/
YAHOO.widget.ContainerEffect.prototype.animateIn = function() {
	this.beforeAnimateInEvent.fire();
	this.animIn.animate();
};

/**
* Triggers the out-animation.
* @method animateOut
*/
YAHOO.widget.ContainerEffect.prototype.animateOut = function() {
	this.beforeAnimateOutEvent.fire();
	this.animOut.animate();
};

/**
* The default onStart handler for the in-animation.
* @method handleStartAnimateIn
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleStartAnimateIn = function(type, args, obj) { };
/**
* The default onTween handler for the in-animation.
* @method handleTweenAnimateIn
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleTweenAnimateIn = function(type, args, obj) { };
/**
* The default onComplete handler for the in-animation.
* @method handleCompleteAnimateIn
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleCompleteAnimateIn = function(type, args, obj) { };

/**
* The default onStart handler for the out-animation.
* @method handleStartAnimateOut
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleStartAnimateOut = function(type, args, obj) { };
/**
* The default onTween handler for the out-animation.
* @method handleTweenAnimateOut
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleTweenAnimateOut = function(type, args, obj) { };
/**
* The default onComplete handler for the out-animation.
* @method handleCompleteAnimateOut
* @param {String} type	The CustomEvent type
* @param {Object[]}	args	The CustomEvent arguments
* @param {Object} obj	The scope object
*/
YAHOO.widget.ContainerEffect.prototype.handleCompleteAnimateOut = function(type, args, obj) { };

/**
* Returns a string representation of the object.
* @method toString
* @return {String}	The string representation of the ContainerEffect
*/
YAHOO.widget.ContainerEffect.prototype.toString = function() {
	var output = "ContainerEffect";
	if (this.overlay) {
		output += " [" + this.overlay.toString() + "]";
	}
	return output;
};

/**
* A pre-configured ContainerEffect instance that can be used for fading an overlay in and out.
* @method FADE
* @static
* @param {Overlay}	The Overlay object to animate
* @param {Number}	The duration of the animation
* @return {ContainerEffect}	The configured ContainerEffect object
*/
YAHOO.widget.ContainerEffect.FADE = function(overlay, dur) {
	var fade = new YAHOO.widget.ContainerEffect(overlay, { attributes:{opacity: {from:0, to:1}}, duration:dur, method:YAHOO.util.Easing.easeIn }, { attributes:{opacity: {to:0}}, duration:dur, method:YAHOO.util.Easing.easeOut}, overlay.element );

	fade.handleStartAnimateIn = function(type,args,obj) {
		YAHOO.util.Dom.addClass(obj.overlay.element, "hide-select");

		if (! obj.overlay.underlay) {
			obj.overlay.cfg.refireEvent("underlay");
		}

		if (obj.overlay.underlay) {
			obj.initialUnderlayOpacity = YAHOO.util.Dom.getStyle(obj.overlay.underlay, "opacity");
			obj.overlay.underlay.style.filter = null;
		}

		YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "visible");
		YAHOO.util.Dom.setStyle(obj.overlay.element, "opacity", 0);
	};

	fade.handleCompleteAnimateIn = function(type,args,obj) {
		YAHOO.util.Dom.removeClass(obj.overlay.element, "hide-select");

		if (obj.overlay.element.style.filter) {
			obj.overlay.element.style.filter = null;
		}

		if (obj.overlay.underlay) {
			YAHOO.util.Dom.setStyle(obj.overlay.underlay, "opacity", obj.initialUnderlayOpacity);
		}

		obj.overlay.cfg.refireEvent("iframe");
		obj.animateInCompleteEvent.fire();
	};

	fade.handleStartAnimateOut = function(type, args, obj) {
		YAHOO.util.Dom.addClass(obj.overlay.element, "hide-select");

		if (obj.overlay.underlay) {
			obj.overlay.underlay.style.filter = null;
		}
	};

	fade.handleCompleteAnimateOut =  function(type, args, obj) {
		YAHOO.util.Dom.removeClass(obj.overlay.element, "hide-select");
		if (obj.overlay.element.style.filter) {
			obj.overlay.element.style.filter = null;
		}
		YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "hidden");
		YAHOO.util.Dom.setStyle(obj.overlay.element, "opacity", 1);

		obj.overlay.cfg.refireEvent("iframe");

		obj.animateOutCompleteEvent.fire();
	};

	fade.init();
	return fade;
};


/**
* A pre-configured ContainerEffect instance that can be used for sliding an overlay in and out.
* @method SLIDE
* @static
* @param {Overlay}	The Overlay object to animate
* @param {Number}	The duration of the animation
* @return {ContainerEffect}	The configured ContainerEffect object
*/
YAHOO.widget.ContainerEffect.SLIDE = function(overlay, dur) {
	var x = overlay.cfg.getProperty("x") || YAHOO.util.Dom.getX(overlay.element);
	var y = overlay.cfg.getProperty("y") || YAHOO.util.Dom.getY(overlay.element);

	var clientWidth = YAHOO.util.Dom.getClientWidth();
	var offsetWidth = overlay.element.offsetWidth;

	var slide = new YAHOO.widget.ContainerEffect(overlay, {
															attributes:{ points: { to:[x, y] } },
															duration:dur,
															method:YAHOO.util.Easing.easeIn
														},
														{
															attributes:{ points: { to:[(clientWidth+25), y] } },
															duration:dur,
															method:YAHOO.util.Easing.easeOut
														},
														overlay.element,
														YAHOO.util.Motion);


	slide.handleStartAnimateIn = function(type,args,obj) {
		obj.overlay.element.style.left = (-25-offsetWidth) + "px";
		obj.overlay.element.style.top  = y + "px";
	};

	slide.handleTweenAnimateIn = function(type, args, obj) {


		var pos = YAHOO.util.Dom.getXY(obj.overlay.element);

		var currentX = pos[0];
		var currentY = pos[1];

		if (YAHOO.util.Dom.getStyle(obj.overlay.element, "visibility") == "hidden" && currentX < x) {
			YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "visible");
		}

		obj.overlay.cfg.setProperty("xy", [currentX,currentY], true);
		obj.overlay.cfg.refireEvent("iframe");
	};

	slide.handleCompleteAnimateIn = function(type, args, obj) {
		obj.overlay.cfg.setProperty("xy", [x,y], true);
		obj.startX = x;
		obj.startY = y;
		obj.overlay.cfg.refireEvent("iframe");
		obj.animateInCompleteEvent.fire();
	};

	slide.handleStartAnimateOut = function(type, args, obj) {
		var vw = YAHOO.util.Dom.getViewportWidth();

		var pos = YAHOO.util.Dom.getXY(obj.overlay.element);

		var yso = pos[1];

		var currentTo = obj.animOut.attributes.points.to;
		obj.animOut.attributes.points.to = [(vw+25), yso];
	};

	slide.handleTweenAnimateOut = function(type, args, obj) {
		var pos = YAHOO.util.Dom.getXY(obj.overlay.element);

		var xto = pos[0];
		var yto = pos[1];

		obj.overlay.cfg.setProperty("xy", [xto,yto], true);
		obj.overlay.cfg.refireEvent("iframe");
	};

	slide.handleCompleteAnimateOut = function(type, args, obj) {
		YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "hidden");

		obj.overlay.cfg.setProperty("xy", [x,y]);
		obj.animateOutCompleteEvent.fire();
	};

	slide.init();
	return slide;
};
/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ (function(){var _1=YAHOO.util.Event;var _2=YAHOO.util.Dom;YAHOO.util.DragDrop=function(id,_4,_5){if(id){this.init(id,_4,_5);}};YAHOO.util.DragDrop.prototype={id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,b4StartDrag:function(x,y){},startDrag:function(x,y){},b4Drag:function(e){},onDrag:function(e){},onDragEnter:function(e,id){},b4DragOver:function(e){},onDragOver:function(e,id){},b4DragOut:function(e){},onDragOut:function(e,id){},b4DragDrop:function(e){},onDragDrop:function(e,id){},onInvalidDrop:function(e){},b4EndDrag:function(e){},endDrag:function(e){},b4MouseDown:function(e){},onMouseDown:function(e){},onMouseUp:function(e){},onAvailable:function(){},getEl:function(){if(!this._domRef){this._domRef=_2.get(this.id);}return this._domRef;},getDragEl:function(){return _2.get(this.dragElId);},init:function(id,_9,_10){this.initTarget(id,_9,_10);_1.on(this.id,"mousedown",this.handleMouseDown,this,true);},initTarget:function(id,_11,_12){this.config=_12||{};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.id=id;this.addToGroup((_11)?_11:"default");this.handleElId=id;_1.onAvailable(id,this.handleOnAvailable,this,true);this.setDragElId(id);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();},applyConfig:function(){this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false);},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable();},setPadding:function(_13,_14,_15,_16){if(!_14&&0!==_14){this.padding=[_13,_13,_13,_13];}else{if(!_15&&0!==_15){this.padding=[_13,_14,_13,_14];}else{this.padding=[_13,_14,_15,_16];}}},setInitPosition:function(_17,_18){var el=this.getEl();if(!this.DDM.verifyEl(el)){return;}var dx=_17||0;var dy=_18||0;var p=_2.getXY(el);this.initPageX=p[0]-dx;this.initPageY=p[1]-dy;this.lastPageX=p[0];this.lastPageY=p[1];this.setStartPosition(p);},setStartPosition:function(pos){var p=pos||_2.getXY(this.getEl());this.deltaSetXY=null;this.startPageX=p[0];this.startPageY=p[1];},addToGroup:function(_24){this.groups[_24]=true;this.DDM.regDragDrop(this,_24);},removeFromGroup:function(_25){if(this.groups[_25]){delete this.groups[_25];}this.DDM.removeDDFromGroup(this,_25);},setDragElId:function(id){this.dragElId=id;},setHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.handleElId=id;this.DDM.regHandle(this.id,id);},setOuterHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}_1.on(id,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(id);this.hasOuterHandles=true;},unreg:function(){_1.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return (this.DDM.isLocked()||this.locked);},handleMouseDown:function(e,oDD){var _27=e.which||e.button;if(this.primaryButtonOnly&&_27>1){return;}if(this.isLocked()){return;}this.DDM.refreshCache(this.groups);var pt=new YAHOO.util.Point(_1.getPageX(e),_1.getPageY(e));if(!this.hasOuterHandles&&!this.DDM.isOverTarget(pt,this)){}else{if(this.clickValidator(e)){this.setStartPosition();this.b4MouseDown(e);this.onMouseDown(e);this.DDM.handleMouseDown(e,this);this.DDM.stopEvent(e);}else{}}},clickValidator:function(e){var _29=_1.getTarget(e);return (this.isValidHandleChild(_29)&&(this.id==this.handleElId||this.DDM.handleWasClicked(_29,this.id)));},addInvalidHandleType:function(_30){var _31=_30.toUpperCase();this.invalidHandleTypes[_31]=_31;},addInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.invalidHandleIds[id]=id;},addInvalidHandleClass:function(_32){this.invalidHandleClasses.push(_32);},removeInvalidHandleType:function(_33){var _34=_33.toUpperCase();delete this.invalidHandleTypes[_34];},removeInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}delete this.invalidHandleIds[id];},removeInvalidHandleClass:function(_35){for(var i=0,len=this.invalidHandleClasses.length;i<len;++i){if(this.invalidHandleClasses[i]==_35){delete this.invalidHandleClasses[i];}}},isValidHandleChild:function(_37){var _38=true;var _39;try{_39=_37.nodeName.toUpperCase();}catch(e){_39=_37.nodeName;}_38=_38&&!this.invalidHandleTypes[_39];_38=_38&&!this.invalidHandleIds[_37.id];for(var i=0,len=this.invalidHandleClasses.length;_38&&i<len;++i){_38=!_2.hasClass(_37,this.invalidHandleClasses[i]);}return _38;},setXTicks:function(_40,_41){this.xTicks=[];this.xTickSize=_41;var _42={};for(var i=this.initPageX;i>=this.minX;i=i-_41){if(!_42[i]){this.xTicks[this.xTicks.length]=i;_42[i]=true;}}for(i=this.initPageX;i<=this.maxX;i=i+_41){if(!_42[i]){this.xTicks[this.xTicks.length]=i;_42[i]=true;}}this.xTicks.sort(this.DDM.numericSort);},setYTicks:function(_43,_44){this.yTicks=[];this.yTickSize=_44;var _45={};for(var i=this.initPageY;i>=this.minY;i=i-_44){if(!_45[i]){this.yTicks[this.yTicks.length]=i;_45[i]=true;}}for(i=this.initPageY;i<=this.maxY;i=i+_44){if(!_45[i]){this.yTicks[this.yTicks.length]=i;_45[i]=true;}}this.yTicks.sort(this.DDM.numericSort);},setXConstraint:function(_46,_47,_48){this.leftConstraint=_46;this.rightConstraint=_47;this.minX=this.initPageX-_46;this.maxX=this.initPageX+_47;if(_48){this.setXTicks(this.initPageX,_48);}this.constrainX=true;},clearConstraints:function(){this.constrainX=false;this.constrainY=false;this.clearTicks();},clearTicks:function(){this.xTicks=null;this.yTicks=null;this.xTickSize=0;this.yTickSize=0;},setYConstraint:function(iUp,_50,_51){this.topConstraint=iUp;this.bottomConstraint=_50;this.minY=this.initPageY-iUp;this.maxY=this.initPageY+_50;if(_51){this.setYTicks(this.initPageY,_51);}this.constrainY=true;},resetConstraints:function(){if(this.initPageX||this.initPageX===0){var dx=(this.maintainOffset)?this.lastPageX-this.initPageX:0;var dy=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(dx,dy);}else{this.setInitPosition();}if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize);}},getTick:function(val,_53){if(!_53){return val;}else{if(_53[0]>=val){return _53[0];}else{for(var i=0,len=_53.length;i<len;++i){var _54=i+1;if(_53[_54]&&_53[_54]>=val){var _55=val-_53[i];var _56=_53[_54]-val;return (_56>_55)?_53[i]:_53[_54];}}return _53[_53.length-1];}}},toString:function(){return ("DragDrop "+this.id);}};})();if(!YAHOO.util.DragDropMgr){YAHOO.util.DragDropMgr=function(){var _57=YAHOO.util.Event;return {ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initalized:false,locked:false,init:function(){this.initialized=true;},POINT:0,INTERSECT:1,mode:0,_execOnAll:function(_58,_59){for(var i in this.ids){for(var j in this.ids[i]){var oDD=this.ids[i][j];if(!this.isTypeOfDD(oDD)){continue;}oDD[_58].apply(oDD,_59);}}},_onLoad:function(){this.init();_57.on(document,"mouseup",this.handleMouseUp,this,true);_57.on(document,"mousemove",this.handleMouseMove,this,true);_57.on(window,"unload",this._onUnload,this,true);_57.on(window,"resize",this._onResize,this,true);},_onResize:function(e){this._execOnAll("resetConstraints",[]);},lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isLocked:function(){return this.locked;},locationCache:{},useCache:true,clickPixelThresh:3,clickTimeThresh:1000,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,regDragDrop:function(oDD,_61){if(!this.initialized){this.init();}if(!this.ids[_61]){this.ids[_61]={};}this.ids[_61][oDD.id]=oDD;},removeDDFromGroup:function(oDD,_62){if(!this.ids[_62]){this.ids[_62]={};}var obj=this.ids[_62];if(obj&&obj[oDD.id]){delete obj[oDD.id];}},_remove:function(oDD){for(var g in oDD.groups){if(g&&this.ids[g][oDD.id]){delete this.ids[g][oDD.id];}}delete this.handleIds[oDD.id];},regHandle:function(_65,_66){if(!this.handleIds[_65]){this.handleIds[_65]={};}this.handleIds[_65][_66]=_66;},isDragDrop:function(id){return (this.getDDById(id))?true:false;},getRelated:function(_67,_68){var _69=[];for(var i in _67.groups){for(j in this.ids[i]){var dd=this.ids[i][j];if(!this.isTypeOfDD(dd)){continue;}if(!_68||dd.isTarget){_69[_69.length]=dd;}}}return _69;},isLegalTarget:function(oDD,_71){var _72=this.getRelated(oDD,true);for(var i=0,len=_72.length;i<len;++i){if(_72[i].id==_71.id){return true;}}return false;},isTypeOfDD:function(oDD){return (oDD&&oDD.__ygDragDrop);},isHandle:function(_73,_74){return (this.handleIds[_73]&&this.handleIds[_73][_74]);},getDDById:function(id){for(var i in this.ids){if(this.ids[i][id]){return this.ids[i][id];}}return null;},handleMouseDown:function(e,oDD){this.currentTarget=YAHOO.util.Event.getTarget(e);this.dragCurrent=oDD;var el=oDD.getEl();this.startX=YAHOO.util.Event.getPageX(e);this.startY=YAHOO.util.Event.getPageY(e);this.deltaX=this.startX-el.offsetLeft;this.deltaY=this.startY-el.offsetTop;this.dragThreshMet=false;this.clickTimeout=setTimeout(function(){var DDM=YAHOO.util.DDM;DDM.startDrag(DDM.startX,DDM.startY);},this.clickTimeThresh);},startDrag:function(x,y){clearTimeout(this.clickTimeout);if(this.dragCurrent){this.dragCurrent.b4StartDrag(x,y);this.dragCurrent.startDrag(x,y);}this.dragThreshMet=true;},handleMouseUp:function(e){if(!this.dragCurrent){return;}clearTimeout(this.clickTimeout);if(this.dragThreshMet){this.fireEvents(e,true);}else{}this.stopDrag(e);this.stopEvent(e);},stopEvent:function(e){if(this.stopPropagation){YAHOO.util.Event.stopPropagation(e);}if(this.preventDefault){YAHOO.util.Event.preventDefault(e);}},stopDrag:function(e){if(this.dragCurrent){if(this.dragThreshMet){this.dragCurrent.b4EndDrag(e);this.dragCurrent.endDrag(e);}this.dragCurrent.onMouseUp(e);}this.dragCurrent=null;this.dragOvers={};},handleMouseMove:function(e){if(!this.dragCurrent){return true;}if(YAHOO.util.Event.isIE&&!e.button){this.stopEvent(e);return this.handleMouseUp(e);}if(!this.dragThreshMet){var _76=Math.abs(this.startX-YAHOO.util.Event.getPageX(e));var _77=Math.abs(this.startY-YAHOO.util.Event.getPageY(e));if(_76>this.clickPixelThresh||_77>this.clickPixelThresh){this.startDrag(this.startX,this.startY);}}if(this.dragThreshMet){this.dragCurrent.b4Drag(e);this.dragCurrent.onDrag(e);this.fireEvents(e,false);}this.stopEvent(e);return true;},fireEvents:function(e,_78){var dc=this.dragCurrent;if(!dc||dc.isLocked()){return;}var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);var pt=new YAHOO.util.Point(x,y);var _80=[];var _81=[];var _82=[];var _83=[];var _84=[];for(var i in this.dragOvers){var ddo=this.dragOvers[i];if(!this.isTypeOfDD(ddo)){continue;}if(!this.isOverTarget(pt,ddo,this.mode)){_81.push(ddo);}_80[i]=true;delete this.dragOvers[i];}for(var _86 in dc.groups){if("string"!=typeof _86){continue;}for(i in this.ids[_86]){var oDD=this.ids[_86][i];if(!this.isTypeOfDD(oDD)){continue;}if(oDD.isTarget&&!oDD.isLocked()&&oDD!=dc){if(this.isOverTarget(pt,oDD,this.mode)){if(_78){_83.push(oDD);}else{if(!_80[oDD.id]){_84.push(oDD);}else{_82.push(oDD);}this.dragOvers[oDD.id]=oDD;}}}}}if(this.mode){if(_81.length){dc.b4DragOut(e,_81);dc.onDragOut(e,_81);}if(_84.length){dc.onDragEnter(e,_84);}if(_82.length){dc.b4DragOver(e,_82);dc.onDragOver(e,_82);}if(_83.length){dc.b4DragDrop(e,_83);dc.onDragDrop(e,_83);}}else{var len=0;for(i=0,len=_81.length;i<len;++i){dc.b4DragOut(e,_81[i].id);dc.onDragOut(e,_81[i].id);}for(i=0,len=_84.length;i<len;++i){dc.onDragEnter(e,_84[i].id);}for(i=0,len=_82.length;i<len;++i){dc.b4DragOver(e,_82[i].id);dc.onDragOver(e,_82[i].id);}for(i=0,len=_83.length;i<len;++i){dc.b4DragDrop(e,_83[i].id);dc.onDragDrop(e,_83[i].id);}}if(_78&&!_83.length){dc.onInvalidDrop(e);}},getBestMatch:function(dds){var _89=null;var len=dds.length;if(len==1){_89=dds[0];}else{for(var i=0;i<len;++i){var dd=dds[i];if(dd.cursorIsOver){_89=dd;break;}else{if(!_89||_89.overlap.getArea()<dd.overlap.getArea()){_89=dd;}}}}return _89;},refreshCache:function(_90){for(var _91 in _90){if("string"!=typeof _91){continue;}for(var i in this.ids[_91]){var oDD=this.ids[_91][i];if(this.isTypeOfDD(oDD)){var loc=this.getLocation(oDD);if(loc){this.locationCache[oDD.id]=loc;}else{delete this.locationCache[oDD.id];}}}}},verifyEl:function(el){try{if(el){var _93=el.offsetParent;if(_93){return true;}}}catch(e){}return false;},getLocation:function(oDD){if(!this.isTypeOfDD(oDD)){return null;}var el=oDD.getEl(),pos,x1,x2,y1,y2,t,r,b,l;try{pos=YAHOO.util.Dom.getXY(el);}catch(e){}if(!pos){return null;}x1=pos[0];x2=x1+el.offsetWidth;y1=pos[1];y2=y1+el.offsetHeight;t=y1-oDD.padding[0];r=x2+oDD.padding[1];b=y2+oDD.padding[2];l=x1-oDD.padding[3];return new YAHOO.util.Region(t,r,b,l);},isOverTarget:function(pt,_94,_95){var loc=this.locationCache[_94.id];if(!loc||!this.useCache){loc=this.getLocation(_94);this.locationCache[_94.id]=loc;}if(!loc){return false;}_94.cursorIsOver=loc.contains(pt);var dc=this.dragCurrent;if(!dc||!dc.getTargetCoord||(!_95&&!dc.constrainX&&!dc.constrainY)){return _94.cursorIsOver;}_94.overlap=null;var pos=dc.getTargetCoord(pt.x,pt.y);var el=dc.getDragEl();var _96=new YAHOO.util.Region(pos.y,pos.x+el.offsetWidth,pos.y+el.offsetHeight,pos.x);var _97=_96.intersect(loc);if(_97){_94.overlap=_97;return (_95)?true:_94.cursorIsOver;}else{return false;}},_onUnload:function(e,me){this.unregAll();},unregAll:function(){if(this.dragCurrent){this.stopDrag();this.dragCurrent=null;}this._execOnAll("unreg",[]);for(i in this.elementCache){delete this.elementCache[i];}this.elementCache={};this.ids={};},elementCache:{},getElWrapper:function(id){var _99=this.elementCache[id];if(!_99||!_99.el){_99=this.elementCache[id]=new this.ElementWrapper(YAHOO.util.Dom.get(id));}return _99;},getElement:function(id){return YAHOO.util.Dom.get(id);},getCss:function(id){var el=YAHOO.util.Dom.get(id);return (el)?el.style:null;},ElementWrapper:function(el){this.el=el||null;this.id=this.el&&el.id;this.css=this.el&&el.style;},getPosX:function(el){return YAHOO.util.Dom.getX(el);},getPosY:function(el){return YAHOO.util.Dom.getY(el);},swapNode:function(n1,n2){if(n1.swapNode){n1.swapNode(n2);}else{var p=n2.parentNode;var s=n2.nextSibling;if(s==n1){p.insertBefore(n1,n2);}else{if(n2==n1.nextSibling){p.insertBefore(n2,n1);}else{n1.parentNode.replaceChild(n2,n1);p.insertBefore(n1,s);}}}},getScroll:function(){var t,l,dde=document.documentElement,db=document.body;if(dde&&(dde.scrollTop||dde.scrollLeft)){t=dde.scrollTop;l=dde.scrollLeft;}else{if(db){t=db.scrollTop;l=db.scrollLeft;}else{YAHOO.log("could not get scroll property");}}return {top:t,left:l};},getStyle:function(el,_104){return YAHOO.util.Dom.getStyle(el,_104);},getScrollTop:function(){return this.getScroll().top;},getScrollLeft:function(){return this.getScroll().left;},moveToEl:function(_105,_106){var _107=YAHOO.util.Dom.getXY(_106);YAHOO.util.Dom.setXY(_105,_107);},getClientHeight:function(){return YAHOO.util.Dom.getViewportHeight();},getClientWidth:function(){return YAHOO.util.Dom.getViewportWidth();},numericSort:function(a,b){return (a-b);},_timeoutCount:0,_addListeners:function(){var DDM=YAHOO.util.DDM;if(YAHOO.util.Event&&document){DDM._onLoad();}else{if(DDM._timeoutCount>2000){}else{setTimeout(DDM._addListeners,10);if(document&&document.body){DDM._timeoutCount+=1;}}}},handleWasClicked:function(node,id){if(this.isHandle(id,node.id)){return true;}else{var p=node.parentNode;while(p){if(this.isHandle(id,p.id)){return true;}else{p=p.parentNode;}}}return false;}};}();YAHOO.util.DDM=YAHOO.util.DragDropMgr;YAHOO.util.DDM._addListeners();}YAHOO.util.DD=function(id,_111,_112){if(id){this.init(id,_111,_112);}};YAHOO.extend(YAHOO.util.DD,YAHOO.util.DragDrop,{scroll:true,autoOffset:function(_113,_114){var x=_113-this.startPageX;var y=_114-this.startPageY;this.setDelta(x,y);},setDelta:function(_115,_116){this.deltaX=_115;this.deltaY=_116;},setDragElPos:function(_117,_118){var el=this.getDragEl();this.alignElWithMouse(el,_117,_118);},alignElWithMouse:function(el,_119,_120){var _121=this.getTargetCoord(_119,_120);if(!this.deltaSetXY){var _122=[_121.x,_121.y];YAHOO.util.Dom.setXY(el,_122);var _123=parseInt(YAHOO.util.Dom.getStyle(el,"left"),10);var _124=parseInt(YAHOO.util.Dom.getStyle(el,"top"),10);this.deltaSetXY=[_123-_121.x,_124-_121.y];}else{YAHOO.util.Dom.setStyle(el,"left",(_121.x+this.deltaSetXY[0])+"px");YAHOO.util.Dom.setStyle(el,"top",(_121.y+this.deltaSetXY[1])+"px");}this.cachePosition(_121.x,_121.y);this.autoScroll(_121.x,_121.y,el.offsetHeight,el.offsetWidth);},cachePosition:function(_125,_126){if(_125){this.lastPageX=_125;this.lastPageY=_126;}else{var _127=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=_127[0];this.lastPageY=_127[1];}},autoScroll:function(x,y,h,w){if(this.scroll){var _130=this.DDM.getClientHeight();var _131=this.DDM.getClientWidth();var st=this.DDM.getScrollTop();var sl=this.DDM.getScrollLeft();var bot=h+y;var _135=w+x;var _136=(_130+st-y-this.deltaY);var _137=(_131+sl-x-this.deltaX);var _138=40;var _139=(document.all)?80:30;if(bot>_130&&_136<_138){window.scrollTo(sl,st+_139);}if(y<st&&st>0&&y-st<_138){window.scrollTo(sl,st-_139);}if(_135>_131&&_137<_138){window.scrollTo(sl+_139,st);}if(x<sl&&sl>0&&x-sl<_138){window.scrollTo(sl-_139,st);}}},getTargetCoord:function(_140,_141){var x=_140-this.deltaX;var y=_141-this.deltaY;if(this.constrainX){if(x<this.minX){x=this.minX;}if(x>this.maxX){x=this.maxX;}}if(this.constrainY){if(y<this.minY){y=this.minY;}if(y>this.maxY){y=this.maxY;}}x=this.getTick(x,this.xTicks);y=this.getTick(y,this.yTicks);return {x:x,y:y};},applyConfig:function(){YAHOO.util.DD.superclass.applyConfig.call(this);this.scroll=(this.config.scroll!==false);},b4MouseDown:function(e){this.autoOffset(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));},b4Drag:function(e){this.setDragElPos(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));},toString:function(){return ("DD "+this.id);}});YAHOO.util.DDProxy=function(id,_142,_143){if(id){this.init(id,_142,_143);this.initFrame();}};YAHOO.util.DDProxy.dragElId="ygddfdiv";YAHOO.extend(YAHOO.util.DDProxy,YAHOO.util.DD,{resizeFrame:true,centerFrame:false,createFrame:function(){var self=this;var body=document.body;if(!body||!body.firstChild){setTimeout(function(){self.createFrame();},50);return;}var div=this.getDragEl();if(!div){div=document.createElement("div");div.id=this.dragElId;var s=div.style;s.position="absolute";s.visibility="hidden";s.cursor="move";s.border="2px solid #aaa";s.zIndex=999;body.insertBefore(div,body.firstChild);}},initFrame:function(){this.createFrame();},applyConfig:function(){YAHOO.util.DDProxy.superclass.applyConfig.call(this);this.resizeFrame=(this.config.resizeFrame!==false);this.centerFrame=(this.config.centerFrame);this.setDragElId(this.config.dragElId||YAHOO.util.DDProxy.dragElId);},showFrame:function(_147,_148){var el=this.getEl();var _149=this.getDragEl();var s=_149.style;this._resizeProxy();if(this.centerFrame){this.setDelta(Math.round(parseInt(s.width,10)/2),Math.round(parseInt(s.height,10)/2));}this.setDragElPos(_147,_148);YAHOO.util.Dom.setStyle(_149,"visibility","visible");},_resizeProxy:function(){if(this.resizeFrame){var DOM=YAHOO.util.Dom;var el=this.getEl();var _151=this.getDragEl();var bt=parseInt(DOM.getStyle(_151,"borderTopWidth"),10);var br=parseInt(DOM.getStyle(_151,"borderRightWidth"),10);var bb=parseInt(DOM.getStyle(_151,"borderBottomWidth"),10);var bl=parseInt(DOM.getStyle(_151,"borderLeftWidth"),10);if(isNaN(bt)){bt=0;}if(isNaN(br)){br=0;}if(isNaN(bb)){bb=0;}if(isNaN(bl)){bl=0;}var _156=Math.max(0,el.offsetWidth-br-bl);var _157=Math.max(0,el.offsetHeight-bt-bb);DOM.setStyle(_151,"width",_156+"px");DOM.setStyle(_151,"height",_157+"px");}},b4MouseDown:function(e){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.autoOffset(x,y);this.setDragElPos(x,y);},b4StartDrag:function(x,y){this.showFrame(x,y);},b4EndDrag:function(e){YAHOO.util.Dom.setStyle(this.getDragEl(),"visibility","hidden");},endDrag:function(e){var DOM=YAHOO.util.Dom;var lel=this.getEl();var del=this.getDragEl();DOM.setStyle(del,"visibility","");DOM.setStyle(lel,"visibility","hidden");YAHOO.util.DDM.moveToEl(lel,del);DOM.setStyle(del,"visibility","hidden");DOM.setStyle(lel,"visibility","");},toString:function(){return ("DDProxy "+this.id);}});YAHOO.util.DDTarget=function(id,_160,_161){if(id){this.initTarget(id,_160,_161);}};YAHOO.extend(YAHOO.util.DDTarget,YAHOO.util.DragDrop,{toString:function(){return ("DDTarget "+this.id);}});
var ttBgColor="#e6ecff";var ttBgImg="";var ttBorderColor="#003399";var ttBorderWidth=1;var ttDelay=500;var ttFontColor="#000066";var ttFontFace="arial,helvetica,sans-serif";var ttFontSize="11px";var ttFontWeight="normal";var ttOffsetX=12;var ttOffsetY=15;var ttOpacity=100;var ttPadding=3;var ttShadowColor="";var ttShadowWidth=0;var ttTemp=0;var ttTextAlign="left";var ttTitleColor="#ffffff";var ttWidth=300;var tt_tags=new Array("a","area","b","big","caption","center","code","dd","div","dl","dt","em","h1","h2","h3","h4","h5","h6","i","img","input","li","map","ol","p","pre","s","small","span","strike","strong","sub","sup","table","td","th","tr","tt","u","var","ul","layer");var tt_obj,tt_objW=0,tt_objH=0,tt_objX=0,tt_objY=0,tt_offX=0,tt_offY=0,xlim=0,ylim=0,tt_sup=false,tt_sticky=false,tt_wait=false,tt_act=false,tt_sub=false,tt_u="undefined",tt_mf,tt_inputs=new Array(),tt_tag=null;var tt_db,tt_n=navigator.userAgent.toLowerCase();function tt_init(){tt_db=(document.compatMode&&document.compatMode!="BackCompat")?document.documentElement:document.body?document.body:null;tt_op=!!(window.opera&&document.getElementById),tt_op6=tt_op&&!document.defaultView,tt_op7=tt_op&&!tt_op6,tt_ie=tt_n.indexOf("msie")!=-1&&document.all&&tt_db&&!tt_op,tt_n4=(document.layers&&typeof document.classes!=tt_u)?true:false,tt_n6=(!tt_op&&document.defaultView&&typeof document.defaultView.getComputedStyle!=tt_u),tt_w3c=!tt_ie&&!tt_n6&&!tt_op&&document.getElementById;}function tt_Int(_1){var _2;return isNaN(_2=parseInt(_1))?0:_2;}function wzReplace(_3,_4){var _5="",t_str=this,t_xI;while((t_xI=t_str.indexOf(_3))!=-1){_5+=t_str.substring(0,t_xI)+_4;t_str=t_str.substring(t_xI+_3.length);}return _5+t_str;}String.prototype.wzReplace=wzReplace;function tt_N4Tags(_6,_7,_8){_7=_7||document;_8=_8||new Array();var _9=(_6=="a")?_7.links:_7.layers;for(var z=_9.length;z--;){_8[_8.length]=_9[z];}for(z=_7.layers.length;z--;){_8=tt_N4Tags(_6,_7.layers[z].document,_8);}return _8;}function tt_GetSelects(){if(!tt_op6&&!tt_ie){return;}var _b,t_s=tt_op6?"input":"select";if(document.all){_b=document.all.tags(t_s).length;while(_b--){tt_inputs[_b]=document.all.tags(t_s)[_b];}}else{if(document.getElementsByTagName){_b=document.getElementsByTagName(t_s).length;while(_b--){tt_inputs[_b]=document.getElementsByTagName(t_s)[_b];}}}_b=tt_inputs.length;while(_b--){tt_inputs[_b].vis="";tt_inputs[_b].x=0;tt_inputs[_b].y=0;var _c=tt_inputs[_b];while(_c){tt_inputs[_b].x+=_c.offsetLeft||0;tt_inputs[_b].y+=_c.offsetTop||0;_c=_c.offsetParent;}}}function tt_Htm(tt,_e,_f){var _10=(typeof tt.T_BGCOLOR!=tt_u)?tt.T_BGCOLOR:ttBgColor,t_bgimg=(typeof tt.T_BGIMG!=tt_u)?tt.T_BGIMG:ttBgImg,t_bc=(typeof tt.T_BORDERCOLOR!=tt_u)?tt.T_BORDERCOLOR:ttBorderColor,t_bw=(typeof tt.T_BORDERWIDTH!=tt_u)?tt.T_BORDERWIDTH:ttBorderWidth,t_ff=(typeof tt.T_FONTFACE!=tt_u)?tt.T_FONTFACE:ttFontFace,t_fc=(typeof tt.T_FONTCOLOR!=tt_u)?tt.T_FONTCOLOR:ttFontColor,t_fsz=(typeof tt.T_FONTSIZE!=tt_u)?tt.T_FONTSIZE:ttFontSize,t_fwght=(typeof tt.T_FONTWEIGHT!=tt_u)?tt.T_FONTWEIGHT:ttFontWeight,t_opa=(typeof tt.T_OPACITY!=tt_u)?tt.T_OPACITY:ttOpacity,t_padd=(typeof tt.T_PADDING!=tt_u)?tt.T_PADDING:ttPadding,t_shc=(typeof tt.T_SHADOWCOLOR!=tt_u)?tt.T_SHADOWCOLOR:(ttShadowColor||0),t_shw=(typeof tt.T_SHADOWWIDTH!=tt_u)?tt.T_SHADOWWIDTH:(ttShadowWidth||0),t_algn=(typeof tt.T_TEXTALIGN!=tt_u)?tt.T_TEXTALIGN:ttTextAlign,t_tit=(typeof tt.T_TITLE!=tt_u)?tt.T_TITLE:"",t_titc=(typeof tt.T_TITLECOLOR!=tt_u)?tt.T_TITLECOLOR:ttTitleColor,t_w=(typeof tt.T_WIDTH!=tt_u)?tt.T_WIDTH:ttWidth;if(t_shc||t_shw){t_shc=t_shc||"#cccccc";t_shw=t_shw||5;}if(tt_n4&&(t_fsz=="10px"||t_fsz=="11px")){t_fsz="12px";}var _11=(tt_n4?"":tt_n6?("-moz-opacity:"+(t_opa/100)):tt_ie?("filter:Alpha(opacity="+t_opa+")"):("opacity:"+(t_opa/100)))+";";var t_y="<div id=\""+_e+"\" style=\"position:absolute;z-index:1010;";t_y+="left:0px;top:0px;width:"+(t_w+t_shw)+"px;visibility:"+(tt_n4?"hide":"hidden")+";"+_11+"\">"+"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\""+(t_bc?(" bgcolor=\""+t_bc+"\""):"")+" width=\""+t_w+"\">";if(t_tit){t_y+="<tr><td style=\"padding-left:3px;padding-right:3px;\" align=\""+t_algn+"\"><font color=\""+t_titc+"\" face=\""+t_ff+"\" "+"style=\"color:"+t_titc+";font-family:"+t_ff+";font-size:"+t_fsz+";\"><b>"+(tt_n4?"&nbsp;":"")+t_tit+"</b></font></td></tr>";}t_y+="<tr><td><table style=\"margin: 0 0 0 0\" border=\"0\" cellpadding=\""+t_padd+"\" cellspacing=\""+t_bw+"\" width=\"100%\">"+"<tr><td"+(_10?(" bgcolor=\""+_10+"\""):"")+(t_bgimg?" background=\""+t_bgimg+"\"":"");if(tt_n6){t_y+=" style=\"padding:"+t_padd+"px;\"";}t_y+=" align=\""+t_algn+"\"><font color=\""+t_fc+"\" face=\""+t_ff+"\""+" style=\"color:"+t_fc+";font-family:"+t_ff+";font-size:"+t_fsz+";font-weight:"+t_fwght+";\">";if(t_fwght=="bold"){t_y+="<b>";}t_y+=_f;if(t_fwght=="bold"){t_y+="</b>";}t_y+="</font></td></tr></table></td></tr></table>";if(t_shw){var _13=Math.round(t_shw*1.3);if(tt_n4){t_y+="<layer bgcolor=\""+t_shc+"\" left=\""+t_w+"\" top=\""+_13+"\" width=\""+t_shw+"\" height=\"0\"></layer>"+"<layer bgcolor=\""+t_shc+"\" left=\""+_13+"\" align=\"bottom\" width=\""+(t_w-_13)+"\" height=\""+t_shw+"\"></layer>";}else{_11=tt_n6?"-moz-opacity:0.85;":tt_ie?"filter:Alpha(opacity=85);":"opacity:0.85;";t_y+="<div id=\""+_e+"R\" style=\"position:absolute;background:"+t_shc+";left:"+t_w+"px;top:"+_13+"px;width:"+t_shw+"px;height:1px;overflow:hidden;"+_11+"\"></div>"+"<div style=\"position:relative;background:"+t_shc+";left:"+_13+"px;top:0px;width:"+(t_w-_13)+"px;height:"+t_shw+"px;overflow:hidden;"+_11+"\"></div>";}}return t_y+"</div>";}function tt_EvX(t_e){var t_y=tt_Int(t_e.pageX||t_e.clientX||0)+tt_Int(tt_ie?tt_db.scrollLeft:0)+tt_offX;if(t_y>xlim){t_y=xlim;}var _16=tt_Int(window.pageXOffset||(tt_db?tt_db.scrollLeft:0)||0);if(t_y<_16){t_y=_16;}return t_y;}function tt_EvY(t_e){var t_y=tt_Int(t_e.pageY||t_e.clientY||0)+tt_Int(tt_ie?tt_db.scrollTop:0);if(tt_sup){t_y-=(tt_objH+tt_offY-15);}else{if(t_y>ylim||!tt_sub&&t_y>ylim-24){t_y-=(tt_objH+5);tt_sub=false;}else{t_y+=tt_offY;tt_sub=true;}}if(t_y<0){t_y=0;}return t_y;}function tt_ReleasMov(){if(document.onmousemove==tt_Move){if(!tt_mf&&document.releaseEvents){document.releaseEvents(Event.MOUSEMOVE);}document.onmousemove=tt_mf;}}function tt_HideInput(){if(!(tt_ie||tt_op6)||!tt_inputs){return;}var t_o;var t_i=tt_inputs.length;while(t_i--){t_o=tt_inputs[t_i];if(tt_act&&!t_o.vis&&tt_objX+tt_objW>t_o.x&&tt_objX<t_o.x+t_o.offsetWidth&&tt_objY+tt_objH>t_o.y&&tt_objY<t_o.y+t_o.offsetHeight){t_o.vis=t_o.currentStyle?t_o.currentStyle.visibility:t_o.style.visibility;t_o.style.visibility="hidden";}else{if(t_o.vis){t_o.style.visibility=t_o.vis;t_o.vis="";}}}}function tt_GetDiv(_1b){return (tt_n4?(document.layers[_1b]||null):tt_ie?(document.all[_1b]||null):(document.getElementById(_1b)||null));}function tt_GetDivW(){return tt_Int(tt_n4?tt_obj.clip.width:(tt_obj.style.pixelWidth||tt_obj.offsetWidth));}function tt_GetDivH(){return tt_Int(tt_n4?tt_obj.clip.height:(tt_obj.style.pixelHeight||tt_obj.offsetHeight));}function tt_SetDivZ(){var t_i=tt_obj.style||tt_obj;if(window.dd&&dd.z){t_i.zIndex=Math.max(dd.z+1,t_i.zIndex);}}function tt_SetDivPos(t_x,t_y){var t_i=tt_obj.style||tt_obj;var _20=(tt_op6||tt_n4)?"":"px";t_i.left=(tt_objX=t_x)+_20;t_i.top=(tt_objY=t_y)+_20;}function tt_ShowDiv(t_x){if(tt_n4){tt_obj.visibility=t_x?"show":"hide";}else{tt_obj.style.visibility=t_x?"visible":"hidden";}tt_act=t_x;tt_HideInput();}function tt_OpDeHref(t_e){if(t_e&&t_e.target.hasAttribute("href")){tt_tag=t_e.target;tt_tag.t_href=tt_tag.getAttribute("href");tt_tag.removeAttribute("href");tt_tag.style.cursor="hand";tt_tag.onmousedown=tt_OpReHref;tt_tag.stats=window.status;window.status=tt_tag.t_href;}}function tt_OpReHref(){if(tt_tag){tt_tag.setAttribute("href",tt_tag.t_href);window.status=tt_tag.stats;tt_tag=null;}}function tt_Show(t_e,_24,_25,_26,_27,_28,_29,_2a,_2b,_2c,_2d){if(tt_obj){tt_Hide();}tt_mf=document.onmousemove||null;if(window.dd&&(window.DRAG&&tt_mf==DRAG||window.RESIZE&&tt_mf==RESIZE)){return;}var _2e=document.onmouseup||null;if(tt_mf&&_2e){_2e(t_e);}tt_obj=tt_GetDiv(_24);if(tt_obj){t_e=t_e||window.event;tt_sub=!(tt_sup=_25);tt_sticky=_2c;tt_objW=tt_GetDivW();tt_objH=tt_GetDivH();tt_offX=_28?-(tt_objW+_29):_29;tt_offY=_2a;if(tt_op7){tt_OpDeHref(t_e);}if(tt_n4){if(tt_obj.document.layers.length){var _2f=tt_obj.document.layers[0];_2f.clip.height=tt_objH-Math.round(_2f.clip.width*1.3);}}else{var _30=tt_GetDiv(_24+"R");if(_30){var t_h=tt_objH-tt_Int(_30.style.pixelTop||_30.style.top||0);if(typeof _30.style.pixelHeight!=tt_u){_30.style.pixelHeight=t_h;}else{_30.style.height=t_h+"px";}}}tt_GetSelects();xlim=tt_Int((tt_db&&tt_db.clientWidth)?tt_db.clientWidth:window.innerWidth)+tt_Int(window.pageXOffset||(tt_db?tt_db.scrollLeft:0)||0)-tt_objW-(tt_n4?21:0);ylim=tt_Int(window.innerHeight||tt_db.clientHeight)+tt_Int(window.pageYOffset||(tt_db?tt_db.scrollTop:0)||0)-tt_objH-tt_offY;tt_SetDivZ();if(_27){tt_SetDivPos(tt_Int((_27=_27.split(","))[0]),tt_Int(_27[1]));}else{tt_SetDivPos(tt_EvX(t_e),tt_EvY(t_e));}var _32="tt_ShowDiv('true');";if(_2c){_32+="{"+"tt_ReleasMov();"+"window.tt_upFunc = document.onmouseup || null;"+"if (document.captureEvents) document.captureEvents(Event.MOUSEUP);"+"document.onmouseup = new Function(\"window.setTimeout('tt_Hide();', 10);\");"+"}";}else{if(_2b){_32+="tt_ReleasMov();";}}if(_2d>0){_32+="window.tt_rtm = window.setTimeout('tt_sticky = false; tt_Hide();',"+_2d+");";}window.tt_rdl=window.setTimeout(_32,_26);if(!_27){if(document.captureEvents){document.captureEvents(Event.MOUSEMOVE);}document.onmousemove=tt_Move;}}}var tt_area=false;function tt_Move(_33){if(!tt_obj){return;}if(tt_n6||tt_w3c){if(tt_wait){return;}tt_wait=true;setTimeout("tt_wait = false;",5);}var t_e=_33||window.event;tt_SetDivPos(tt_EvX(t_e),tt_EvY(t_e));if(tt_op6){if(tt_area&&t_e.target.tagName!="AREA"){tt_Hide();}else{if(t_e.target.tagName=="AREA"){tt_area=true;}}}}function tt_Hide(){if(window.tt_obj){if(window.tt_rdl){window.clearTimeout(tt_rdl);}if(!tt_sticky||!tt_act){if(window.tt_rtm){window.clearTimeout(tt_rtm);}tt_ShowDiv(false);tt_SetDivPos(-tt_objW,-tt_objH);tt_obj=null;if(typeof window.tt_upFunc!=tt_u){document.onmouseup=window.tt_upFunc;}}tt_sticky=false;if(tt_op6&&tt_area){tt_area=false;}tt_ReleasMov();if(tt_op7){tt_OpReHref();}tt_HideInput();}}function tt_Init(){if(!(tt_op||tt_n4||tt_n6||tt_ie||tt_w3c)){return;}var htm=tt_n4?"<div style=\"position:absolute;\"></div>":"",tags,t_tj,over,esc="return escape(";var i=tt_tags.length;while(i--){tags=tt_ie?(document.all.tags(tt_tags[i])||1):document.getElementsByTagName?(document.getElementsByTagName(tt_tags[i])||1):(!tt_n4&&tt_tags[i]=="a")?document.links:1;if(tt_n4&&(tt_tags[i]=="a"||tt_tags[i]=="layer")){tags=tt_N4Tags(tt_tags[i]);}var j=tags.length;while(j--){if(typeof (t_tj=tags[j]).onmouseover=="function"&&t_tj.onmouseover.toString().indexOf(esc)!=-1&&!tt_n6||tt_n6&&(over=t_tj.getAttribute("onmouseover"))&&over.indexOf(esc)!=-1){if(over){t_tj.onmouseover=new Function(over);}var txt=unescape(t_tj.onmouseover());htm+=tt_Htm(t_tj,"tOoLtIp"+i+""+j,txt.wzReplace("& ","&"));t_tj.onmouseover=new Function("e","tt_Show(e,"+"\"tOoLtIp"+i+""+j+"\","+(typeof t_tj.T_ABOVE!=tt_u)+","+((typeof t_tj.T_DELAY!=tt_u)?t_tj.T_DELAY:ttDelay)+","+((typeof t_tj.T_FIX!=tt_u)?"\""+t_tj.T_FIX+"\"":"\"\"")+","+(typeof t_tj.T_LEFT!=tt_u)+","+((typeof t_tj.T_OFFSETX!=tt_u)?t_tj.T_OFFSETX:ttOffsetX)+","+((typeof t_tj.T_OFFSETY!=tt_u)?t_tj.T_OFFSETY:ttOffsetY)+","+(typeof t_tj.T_STATIC!=tt_u)+","+(typeof t_tj.T_STICKY!=tt_u)+","+((typeof t_tj.T_TEMP!=tt_u)?t_tj.T_TEMP:ttTemp)+");");t_tj.onmouseout=tt_Hide;if(t_tj.alt){t_tj.alt="";}if(t_tj.title){t_tj.title="";}}}}document.write(htm);}
function movebox(_1,_2,_3){var _4=_1.value.length;if(_4==_3){_2.focus();}}function updateTimeField(_5,_6,_7,_8){_5.value=_6.value+":"+_7.value+":"+_8.value;}function updateComponents(_9,_a,_b,_c){var _d=new StringTokenizer(_9.value,":");if(_d.hasMoreTokens()){_a.value=_d.nextToken();}if(_d.hasMoreTokens()){_b.value=_d.nextToken();}if(_d.hasMoreTokens()){_c.value=_d.nextToken();}}function upRange(_e,_f){var un=_e.value;if(un>_f){return false;}else{return true;}}function checkHour(_11){if(isNaN(_11.value)){alert("The Hour field can only contain numbers\n You entered: "+_11.value);_11.value="00";}if(_11.value.length==2){if(!upRange(_11,23)){alert("The hour field can only contain numbers between 0 and 23");_11.value=23;_11.focus();}}}function checkMinute(_12){if(isNaN(_12.value)){alert("The Minute field can only contain numbers\n You entered: "+_12.value);_12.value="00";}if(_12.value.length==2){if(!upRange(_12,59)){alert("The minute field can only contain numbers between 0 and 59");_12.value=59;_12.focus();}}}function checkSecond(_13){if(isNaN(_13.value)){alert("The Second field can only contain numbers\n You entered: "+_13.value);_13.value="00";}if(_13.value.length==2){if(!upRange(_13,59)){alert("The second field can only contain numbers between 0 and 59");_13.value=59;_13.focus();}}}function StringTokenizer(_14,_15){this.material=_14;this.separator=_15;this.getTokens=getTokens;this.nextToken=nextToken;this.countTokens=countTokens;this.hasMoreTokens=hasMoreTokens;this.tokensReturned=tokensReturned;this.tokens=this.getTokens();this.tokensReturned=0;}function getTokens(){var _16=new Array();var _17;if(this.material.indexOf(this.separator)<0){_16[0]=this.material;return _16;}start=0;end=this.material.indexOf(this.separator,start);var _18=0;var _19;while(this.material.length-start>=1){_17=this.material.substring(start,end);start=end+1;if(this.material.indexOf(this.separator,start+1)<0){end=this.material.length;}else{end=this.material.indexOf(this.separator,start+1);}_19=trim(_17);while(_19.substring(0,this.separator.length)==this.separator){_19=_19.substring(this.separator.length);}_19=trim(_19);if(_19==""){continue;}_16[_18]=_19;_18++;}return _16;}function countTokens(){return this.tokens.length;}function nextToken(){if(this.tokensReturned>=this.tokens.length){return null;}else{var _1a=this.tokens[this.tokensReturned];this.tokensReturned++;return _1a;}}function hasMoreTokens(){if(this.tokensReturned<this.tokens.length){return true;}else{return false;}}function tokensReturned(){return this.tokensReturned;}function trim(_1b){return (_1b.replace(/^\s+|\s+$/g,""));}
/**
 *  Copyright (C) 2006 Orbeon, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the
 *  GNU Lesser General Public License as published by the Free Software Foundation; either version
 *  2.1 of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Lesser General Public License for more details.
 *
 *  The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */

/**
 * Parameters
 */
var XFORMS_DELAY_BEFORE_INCREMENTAL_REQUEST_IN_MS = 500;
var XFORMS_DELAY_BEFORE_FORCE_INCREMENTAL_REQUEST_IN_MS = 2000;
var XFORMS_DELAY_BEFORE_GECKO_COMMUNICATION_ERROR_IN_MS = 5000;
var XFORMS_DELAY_BEFORE_CLOSE_MINIMAL_DIALOG_IN_MS = 5000;
var XFORMS_INTERNAL_SHORT_DELAY_IN_MS = 10;
var XFORMS_DELAY_BEFORE_DISPLAY_LOADING_IN_MS = 500;
var XFORMS_DEBUG_WINDOW_HEIGHT = 600;
var XFORMS_DEBUG_WINDOW_WIDTH = 300;
var XFORMS_LOADING_MIN_TOP_PADDING = 10;

/**
 * Constants
 */
var XFORMS_SEPARATOR_1 = "\xB7";
var XFORMS_SEPARATOR_2 = "-";
var XXFORMS_NAMESPACE_URI = "http://orbeon.org/oxf/xml/xforms";
var BASE_URL = null;
var XFORMS_SERVER_URL = null;
var PATH_TO_JAVASCRIPT_1 = "/ops/javascript/xforms";
var PATH_TO_JAVASCRIPT_2 = "/xforms-server/xforms";
var XFORMS_IS_GECKO = navigator.userAgent.toLowerCase().indexOf("gecko") != -1;
var ELEMENT_TYPE = document.createElement("dummy").nodeType;
var ATTRIBUTE_TYPE = document.createAttribute("dummy").nodeType;
var TEXT_TYPE = document.createTextNode("").nodeType;
var XFORMS_REGEXP_CR = new RegExp("\\r", "g");
var XFORMS_REGEXP_SINGLE_QUOTE = new RegExp("'", "g");
var XFORMS_REGEXP_OPEN_ANGLE = new RegExp("<", "g");
var XFORMS_REGEXP_AMPERSAND = new RegExp("&", "g");
var XFORMS_WIDE_TEXTAREA_MIN_ROWS = 5;

// These variables are not set by default, but if set will be used by this code:
//
//     FCK_CUSTOM_CONFIG
//     USER_LANGUAGE

/* * * * * * Utility functions * * * * * */

var ORBEON = ORBEON || {};
ORBEON.util = ORBEON.util || {};
ORBEON.xforms = ORBEON.xforms || {};

/**
 * Global constants and variable
 */
ORBEON.xforms.Globals = ORBEON.xforms.Globals || {
    // Booleans used for browser detection
    isRenderingEngineGecko : navigator.userAgent.toLowerCase().indexOf("gecko") != -1,    // Firefox
    isRenderingEnginePresto: navigator.userAgent.toLowerCase().indexOf("opera") != -1,    // Opera
    isRenderingEngineWebCore: navigator.userAgent.toLowerCase().indexOf("safari") != -1,  // Safari
    isRenderingEngineWebCore13: navigator.userAgent.indexOf("AppleWebKit/312") != -1,     // Safari 1.3
    isRenderingEngineTridend: navigator.userAgent.toLowerCase().indexOf("msie") != -1     // Internet Explorer
        && navigator.userAgent.toLowerCase().indexOf("opera") == -1,

    /**
     * All the browsers support events in the capture phase, except IE and Safari 1.3. When browser don't support events
     * in the capture phase, we need to register a listener for certain events on the elements itself, instead of
     * just registering the event handler on the window object.
     */
    supportsCaptureEvents: window.addEventListener && navigator.userAgent.indexOf("AppleWebKit/312") == -1,

    eventQueue: [],                      // Events to be sent to the server
    eventsFirstEventTime: 0,             // Time when the first event in the queue was added
    requestForm: null,                   // HTML for the request currently in progress
    requestIgnoreErrors: false,          // Should we ignore errors that result from running this request
    requestInProgress: false,            // Indicates wether an Ajax request is currently in process
    executeEventFunctionQueued: 0,       // Number of ORBEON.xforms.Server.executeNextRequest waiting to be executed
    maskFocusEvents: false,              // Avoid catching focus event when we do it because the server told us to
    previousDOMFocusOut: null,           // We only send a focus out when we receive a focus in, or another focus out
    htmlAreaNames: [],                   // Names of the FCK editors, which we need to reenable them on Firefox
    repeatTreeChildToParent: [],         // Describes the repeat hierarchy
    repeatIndexes: {},                   // The current index for each repeat
    repeatTreeParentToAllChildren: {},   // Map from parent to array with children, used when highlight changes
    inputCalendarCreated: {},            // Maps input id to true when the calendar has been created for that input
    inputCalendarOnclick: {},            // Maps input id to the JSCalendar function that displays the calendar
    inputCalendarCommitedValue: {},      // Maps input id to the value of JSCalendar actually selected by the user
    tooltipLibraryInitialized: false,
    changedIdsRequest: {},               // Id of controls that have been touched by user since the last response was received
    serverValue: {},                     // Values on controls known to the server
    autoCompleteLastKeyCode: {},         // Stores the last key entered for each auto-complete field
    autoCompleteOpen: {},
    loadingOtherPage: false,             // Flag set when loading other page that revents the loading indicator to disappear
    activeControl: null,                 // The currently active control, used to disable hint
    autosizeTextareas: [],               // Ids of the autosize textareas on the page
    fckEditorLoading: false,             // True if  a FCK editor is currently loading
    fckEditorsToLoad: [],                // Queue of FCK editor to load
    dialogs: {},                         // Map for dialogs: id -> YUI dialog object
    dialogMinimalVisible: {},            // Map for minimal dialog id -> boolean isVisible
    dialogMinimalLastMouseOut: {},       // Map for minimal dialog id -> -1 or timestamp of last time the mouse got out of the dialog
    debugDiv: null,                      // Points to the div when debug messages are displayed
    debugLastTime: new Date().getTime(), // Timestamp when the last debug message was printed
    pageLoadedRegistered: false,         // If the page loaded listener has been registered already, to avoid running it more than once
    menuItemsets: {},                    // Maps menu id to structure defining the content of the menu
    menuYui: {},                         // Maps menu id to the YUI object for that menu
    treeYui: {},                         // Maps tree id to the YUI object for that tree

    // Data relative to a form is stored in an array indexed by form id.
    formLoadingLoadingOverlay: {},       // Overlay for the loading indicator
    formLoadingLoadingInitialRightTop:{},// Initial number of pixel between the loading indicator and the top of the page
    formErrorPanel: {},                  // YUI panel used to report errors
    formLoadingNone: {},                 // HTML element with the markup displayed when nothing is displayed
    formStaticState: {},                 // State that does not change for the life of the page
    formDynamicState: {},                // State that changes at every request
    formServerEvents: {},                // Server events information
    formClientState: {}                  // Store for information we want to keep when the page reloaded
};

/**
 * The IE version of those methods does not store anything in the
 * elements as this has some negative side effects like IE reloading
 * background images set with CSS on the element.
 */
ORBEON.util.IEDom = {
    /**
     * Optimized version of YAHOO.util.Dom.hasClass(element, className).
     */
    hasClass: function(element, className) {
        if (element.className == className) {
            // Trivial true case
            return true;
        } else {
            var classes = element.className + XFORMS_SEPARATOR_1;
            if (classes.indexOf(className + " ") == 0) {
                // Starts with the class we look for
                return true;
            }
            if (classes.indexOf(" " + className + " ") != -1) {
                // The class we look for is in the middle
                return true;
            }
            if (classes.indexOf(" " + className + XFORMS_SEPARATOR_1) != -1) {
                // The class we look for is in the end
                return true;
            }
            return false;
        }
    },

    /**
     * Optimized version of YAHOO.util.Dom.addClass(element, className).
     */
    addClass: function(element, className) {
        if (!this.hasClass(element, className))
            element.className = element.className.length == 0 ? className
                    : (element.className + " " + className);
    },

    /**
     * Optimized version of YAHOO.util.Dom.removeClass(element, className).
     */
    removeClass: function(element, className) {
        if (this.hasClass(element, className)) {
            var classes = element.className.split(" ");
            var newClassName = "";
            for (var i = 0; i < classes.length; i++) {
                if (classes[i] != className) {
                    if (newClassName.length > 0) newClassName += " ";
                    newClassName += classes[i];
                }
            }
            // Setting the class with setAttribute("class", newClassName) doesn't work on IE6 and IE7
            element.className = newClassName;
        }
    }
};


/**
 * The hasClass, addClass and removeClass methods use a cache of the
 * classes for a give element for quick lookup. After having parsed the
 * className a first time we store that information in the orbeonClasses
 * map on the given element.
 */
ORBEON.util.MozDom = {
    /**
     * Changes the className on the element based on the information stored in
     * _elementToClasses.
     *
     * @private
     */
    _regenerateClassName: function(element) {
        var newClassName = "";
        for (var existingClassName in element.orbeonClasses) {
            if (element.orbeonClasses[existingClassName]) {
                if (newClassName.length > 0)
                    newClassName += " ";
                newClassName += existingClassName;
            }
        }
        element.className = newClassName;
    },

    /**
     * Optimized version of YAHOO.util.Dom.hasClass(element, className).
     */
    hasClass: function(element, className) {
        if (!element.orbeonClasses) {
            element.orbeonClasses = {};
            var classes = element.className.split(" ");
            for (var i = 0; i < classes.length; i++)
                element.orbeonClasses[classes[i]] = true;
        }
        return element.orbeonClasses[className] == true;
    },

    /**
     * Optimized version of YAHOO.util.Dom.addClass(element, className).
     */
    addClass: function(element, className) {
        if (!this.hasClass(element, className)) {
            element.orbeonClasses[className] = true;
            this._regenerateClassName(element);
        }
    },

    /**
     * Optimized version of YAHOO.util.Dom.removeClass(element, className).
     */
    removeClass: function(element, className) {
        if (this.hasClass(element, className)) {
            element.orbeonClasses[className] = false;
            this._regenerateClassName(element);
        }
    }
};

/**
 *  Utilities to deal with the DOM that supplement what is provided by YAHOO.util.Dom.
 */
ORBEON.util.Dom = {

    ELEMENT_TYPE: 1,

    isElement: function(node) {
        return node.nodeType == this.ELEMENT_TYPE;
    },

    /**
     * This gets around a bug in IE and Opera 8.2 where getElementById by return an element with name equal to
     * the specified id (instead of id equal to the specified id).
     * See: http://www.csb7.com/test/ie_getelementbyid_bug/index.php
     */
    getElementById: function(controlId) {
        var result = document.getElementById(controlId);
        if (result && (result.id != controlId) && document.all) {
            result = null;
            documentAll = document.all[controlId];
            if (documentAll) {
                if (documentAll.length) {
                    for (var i = 0; i < documentAll.length; i++) {
                        if (documentAll[i].id == controlId) {
                            result = documentAll[i];
                            break;
                        }
                    }
                } else {
                    result = documentAll;
                }
            }
        }
        return result;
    },

    getStringValue: function(element) {
        var result = "";
        for (var i = 0; i < element.childNodes.length; i++) {
            var child = element.childNodes[i];
            if (child.nodeType == TEXT_TYPE)
                result += child.nodeValue;
        }
        return result;
    },

    setStringValue: function(element, text) {
        // Remove content
        while (element.childNodes.length > 0)
            element.removeChild(element.firstChild);
        // Add specified text
        var textNode = element.ownerDocument.createTextNode(text);
        element.appendChild(textNode);
    },

    /**
     * Return null when the attribute is not there.
     */
    getAttribute: function(element, name) {
        if (ORBEON.xforms.Globals.isRenderingEngineTridend) {
            // IE incorrectly already return null when the attribute is not there,
            // but this happens to be what we want to do here
            return element.getAttribute(name);
        } else {
            // Other browsers that follow the spec return an empty string when the attribute is not there,
            // so we use hasAttribute() which is not implemented by IE to detect that case.
            if (element.hasAttribute(name)) {
                if (ORBEON.xforms.Globals.isRenderingEngineWebCore) {
                    return ORBEON.util.String.replace(element.getAttribute(name), "&#38;", "&");
                } else {
                    return element.getAttribute(name);
                }
            } else {
                return null;
            }
        }
    },

    getChildElementByIndex: function(parent, position) {
        for (var i = 0; i < parent.childNodes.length; i++) {
            var child = parent.childNodes[i];
            if (ORBEON.util.Dom.isElement(child)) {
                if (position == 0) return child;
                position--;
            }
        }
        return null;
    },

    getChildElementByClass: function(parent, clazz) {
        for (var i = 0; i < parent.childNodes.length; i++) {
            var child = parent.childNodes[i];
            if (ORBEON.util.Dom.isElement(child) && ORBEON.util.Dom.hasClass(child, clazz)) {
                return child;
            }
        }
        return null;
    },

    stringToDom: function(xmlString) {
        if (document.implementation.createDocument) {
            return (new DOMParser()).parseFromString(xmlString, "application/xml")
        } else if (window.ActiveXObject) {
            var dom  = new ActiveXObject("Microsoft.XMLDOM");
            dom.async="false";
            dom.loadXML(xmlString);
            return dom;
        }
        return null;
    },

    clearUploadControl: function(uploadElement) {

        var inputElement = ORBEON.util.Dom.getChildElementByClass(uploadElement, "xforms-upload-select");
        var parentElement = inputElement.parentNode;
        var newInputElement = document.createElement("input");
        ORBEON.util.Dom.addClass(newInputElement, inputElement.className);
        newInputElement.setAttribute("type", inputElement.type);
        newInputElement.setAttribute("name", inputElement.name);
        newInputElement.setAttribute("size", inputElement.size);
        parentElement.replaceChild(newInputElement, inputElement);

        return null;
    }
};

(function () {
    var methodsFrom = ORBEON.xforms.Globals.isRenderingEngineTridend ? ORBEON.util.IEDom : ORBEON.util.MozDom;
    for (var method in methodsFrom)
        ORBEON.util.Dom[method] = methodsFrom[method];
}());

/**
 * General purpose methods on string
 */
ORBEON.util.String = {
    replace: function(text, placeholder, replacement) {
        // Don't try to do the replacement if the string does not contain the placeholder
        return text.indexOf(placeholder) == -1 ? text :
               text.replace(new RegExp(placeholder, "g"), replacement);
    },

    /**
     * Evaluates JavaScript which can contain return caracters we need to remove
     */
    eval: function(javascriptString) {
        javascriptString = ORBEON.util.String.replace(javascriptString, "\n", " ");
        javascriptString = ORBEON.util.String.replace(javascriptString, "\r", " ");
        return eval(javascriptString);
    },

    /**
     * Escape text that apears in an HTML attribute which we use in an innerHTML.
     */
    escapeAttribute: function(text) {
        return ORBEON.util.String.replace(text, '"', '&quot;');
    }
}

/**
 * Utility methods that don't in any other category
 */
ORBEON.util.Utils = {
    logException: function(message, exception) {
        if (typeof console != "undefined") {
            console.log(message); // Normal use; do not remove
            console.log(exception);  // Normal use; do not remove
        }
    }
}

/**
 * This object contains function generally designed to be called from JavaScript code
 * embedded in forms.
 */
ORBEON.xforms.Document = {

    /**
     * Reference: http://www.w3.org/TR/xforms/slice10.html#action-dispatch
     */
    dispatchEvent: function(targetId, eventName, form, bubbles, cancelable, incremental, ignoreErrors) {

        // Use the first XForms form on the page when no form is provided
        if (form == null) {
            for (var formIndex = 0; formIndex < document.forms.length; formIndex++) {
                var candidateForm = document.forms[formIndex];
                if (ORBEON.util.Dom.hasClass(candidateForm, "xforms-form")) {
                    form = candidateForm;
                    break;
                }
            }
        }

        // Create event and fire
        var event = new ORBEON.xforms.Server.Event(form, targetId, null, null, eventName, bubbles, cancelable, ignoreErrors);
        ORBEON.xforms.Server.fireEvents([event], incremental == undefined ? false : incremental);
    },

    /**
     * Returns the value of an XForms control.
     *
     * @param {String} controlId    Id of the control
     */
    getValue: function(controlId) {
        var control = ORBEON.util.Dom.getElementById(controlId);
        return ORBEON.xforms.Controls.getCurrentValue(control);
    },

    /**
     * Set the value of an XForms control.
     *
     * @param {String} controlId    Id of the control
     * @param {String} newValue     New value for the control
     */
    setValue: function(controlId, newValue) {
        var control = ORBEON.util.Dom.getElementById(controlId);
        if (control == null) throw "ORBEON.xforms.Document.setValue: can't find control id '" + controlId + "'";
        xformsFireEvents(new Array(xformsCreateEventArray
                (control, "xxforms-value-change-with-focus-change", String(newValue), null)), false);
    }
};

ORBEON.xforms.Controls = {

    // Returns MIP for a given control
    isRelevant: function (control) { return !ORBEON.util.Dom.hasClass(control, "xforms-disabled"); },
    isReadonly: function (control) { return  ORBEON.util.Dom.hasClass(control, "xforms-readonly"); },
    isRequired: function (control) { return  ORBEON.util.Dom.hasClass(control, "xforms-required"); },
    isValid:    function (control) { return !ORBEON.util.Dom.hasClass(control, "xforms-invalid"); },

    getForm: function(control) {
        if (typeof control.form == "undefined") {
            // There is a span around the control go through parents until we find the form element
            var candidateForm = control;
            while (candidateForm.tagName.toLowerCase() != "form")
                candidateForm = candidateForm.parentNode;
            return candidateForm;
        } else {
            // We have directly a form control
            return control.form;
        }
    },

    getCurrentValue: function(control) {
        if (ORBEON.util.Dom.hasClass(control, "xforms-input") && !ORBEON.util.Dom.hasClass(control, "xforms-type-boolean")) {
            return ORBEON.util.Dom.getChildElementByIndex(control, 1).value;
        } if (ORBEON.util.Dom.hasClass(control, "xforms-select1-open")) {
            return ORBEON.util.Dom.getChildElementByIndex(control, 0).value;
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-select-appearance-full")
                || ORBEON.util.Dom.hasClass(control, "xforms-select1-appearance-full")
                || ORBEON.util.Dom.hasClass(control, "xforms-input-appearance-full")) {
            var inputs = control.getElementsByTagName("input");
            var spanValue = "";
            for (var inputIndex = 0; inputIndex < inputs.length; inputIndex++) {
                var input = inputs[inputIndex];
                if (input.checked) {
                    if (spanValue != "") spanValue += " ";
                    spanValue += input.value;
                }
            }
            return spanValue;
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-select-appearance-compact")
                || ORBEON.util.Dom.hasClass(control, "xforms-select1-appearance-minimal")
                || ORBEON.util.Dom.hasClass(control, "xforms-select1-appearance-compact")
                || ORBEON.util.Dom.hasClass(control, "xforms-input-appearance-minimal")
                || ORBEON.util.Dom.hasClass(control, "xforms-input-appearance-compact")) {
            var options = control.options;
            var selectValue = "";
            for (var optionIndex = 0; optionIndex < options.length; optionIndex++) {
                var option = options[optionIndex];
                if (option.selected) {
                    if (selectValue != "") selectValue += " ";
                    selectValue += option.value;
                }
            }
            return selectValue;
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-textarea")
                        && ORBEON.util.Dom.hasClass(control, "xforms-mediatype-text-html")) {
            var editorInstance = FCKeditorAPI.GetInstance(control.name);
            return editorInstance.GetXHTML();
        } else {
            return control.value;
        }
    },

    _getControlLabel: function(control, className) {
        var candidate = control;
        while (true) {
            if (candidate == null) break;
            if (ORBEON.util.Dom.isElement(candidate)
                    && ORBEON.util.Dom.hasClass(candidate, className)
                    && (candidate.htmlFor == null || candidate.htmlFor == control.id)) break;
            candidate = className == "xforms-label" ? candidate.previousSibling : candidate.nextSibling;
        }
        return candidate;
    },

    _setMessage: function(control, className, message) {
        var label = ORBEON.xforms.Controls._getControlLabel(control, className);
        if (label != null) {
            ORBEON.util.Dom.setStringValue(label, message);
            var helpImage = ORBEON.xforms.Controls._getControlLabel(control, "xforms-help-image");
            if (message == "") {
                ORBEON.util.Dom.addClass(label, "xforms-disabled");
                // If this is the help label, also disable help image
                if (className == "xforms-help")
                    ORBEON.util.Dom.addClass(helpImage, "xforms-disabled");
            } else {
                ORBEON.util.Dom.removeClass(label, "xforms-disabled");
                // If this is the help label, also enable the help image
                if (className == "xforms-help")
                    ORBEON.util.Dom.removeClass(helpImage, "xforms-disabled");
            }
        }
    },

    setLabelMessage: function(control, message) {
        if (ORBEON.util.Dom.hasClass(control, "xforms-trigger")
                || ORBEON.util.Dom.hasClass(control, "xforms-submit")) {
            if (control.tagName.toLowerCase() == "input") {
                // Image
                control.alt = message;
            } else {
                // Link or button
                control.innerHTML = message;
            }
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-dialog")) {
            // Dialog
            var labelDiv = ORBEON.util.Dom.getChildElementByIndex(control, 0);
            ORBEON.util.Dom.setStringValue(labelDiv, message);
        } else {
            ORBEON.xforms.Controls._setMessage(control, "xforms-label", message);
        }
    },

    getHelpMessage: function(control) {
        var helpElement = ORBEON.xforms.Controls._getControlLabel(control, "xforms-help");
        return helpElement == null ? "" : ORBEON.util.Dom.getStringValue(helpElement);
    },

    setHelpMessage: function(control, message) {
        ORBEON.xforms.Controls._setMessage(control, "xforms-help", message);
    },

    setValid: function(control, isValid) {
        // Update class xforms-invalid on the control
        if (isValid) ORBEON.util.Dom.removeClass(control, "xforms-invalid");
        else ORBEON.util.Dom.addClass(control, "xforms-invalid");

        // Update class on alert label
        var alertElement = ORBEON.xforms.Controls._getControlLabel(control, "xforms-alert");
        if (alertElement != null) { // Some controls don't have validity indicator
            if (isValid) {
                ORBEON.util.Dom.removeClass(alertElement, "xforms-alert-active");
                ORBEON.util.Dom.addClass(alertElement, "xforms-alert-inactive");
            } else {
                ORBEON.util.Dom.removeClass(alertElement, "xforms-alert-inactive");
                ORBEON.util.Dom.addClass(alertElement, "xforms-alert-active");
            }
        }
    },

    setRelevant: function(control, isRelevant) {

        if (ORBEON.util.Dom.hasClass(control, "xforms-group-begin-end")) {
            // Case of group delimiters

            // Figure out id of the end delimiter
            var beginMarkerPrefix = "group-begin-";
            var id = control.id.substring(beginMarkerPrefix.length);
            var endMarker = "group-end-" + id;

            // Iterate over nodes until we find the end delimiter
            var current = control.nextSibling;
            while (true) {
                if (ORBEON.util.Dom.isElement(current)) {
                    if (current.id == endMarker) break;
                    if (isRelevant) ORBEON.util.Dom.removeClass(current, "xforms-disabled");
                    else ORBEON.util.Dom.addClass(current, "xforms-disabled");
                }
                current = current.nextSibling;
            }
        } else {
            var elementsToUpdate = [ control,
                ORBEON.xforms.Controls._getControlLabel(control, "xforms-label"),
                ORBEON.xforms.Controls._getControlLabel(control, "xforms-alert")
            ];
            // Also show help if message is not empty
            if (!isRelevant || (isRelevant && ORBEON.xforms.Controls.getHelpMessage(control) != "")) {
                elementsToUpdate.push(ORBEON.xforms.Controls._getControlLabel(control, "xforms-help"));
                elementsToUpdate.push(ORBEON.xforms.Controls._getControlLabel(control, "xforms-help-image"));
            }
            // Also show hint if message is not empty
            if (!isRelevant || (isRelevant && ORBEON.xforms.Controls.getHintMessage(control) != ""))
                elementsToUpdate.push(ORBEON.xforms.Controls._getControlLabel(control, "xforms-hint"));
            for (var elementIndex = 0; elementIndex < elementsToUpdate.length; elementIndex++) {
                var element = elementsToUpdate[elementIndex];
                if (element != null) {
                    if (isRelevant) ORBEON.util.Dom.removeClass(element, "xforms-disabled");
                    else ORBEON.util.Dom.addClass(element, "xforms-disabled");
                }
            }
        }
    },

    getAlertMessage: function(control) {
        var alertElement = ORBEON.xforms.Controls._getControlLabel(control, "xforms-alert");
        return ORBEON.util.Dom.getStringValue(alertElement);
    },

    setAlertMessage: function(control, message) {
        ORBEON.xforms.Controls._setMessage(control, "xforms-alert", message);
    },

    getHintMessage: function(control) {
        if (ORBEON.util.Dom.hasClass(control, "xforms-trigger")) {
            return control.title;
        } else {
            var hintElement = ORBEON.xforms.Controls._getControlLabel(control, "xforms-hint");
            return hintElement == null ? "" : ORBEON.util.Dom.getStringValue(hintElement);
        }
    },

    setHintMessage: function(control, message) {
        if (ORBEON.util.Dom.hasClass(control, "xforms-trigger")) {
            control.title = message;
        } else {
            ORBEON.xforms.Controls._setMessage(control, "xforms-hint", message);
        }
    },

    /**
     * Sets focus to the specified control. This is called by the JavaScript code
     * generated by the server, which we invoke on page load.
     */
    setFocus: function(controlId) {
        var control = ORBEON.util.Dom.getElementById(controlId);
        // To-do: getting elements by position is not very robust
        ORBEON.xforms.Globals.maskFocusEvents = true;
        if (ORBEON.util.Dom.hasClass(control, "xforms-input") && !ORBEON.util.Dom.hasClass(control, "xforms-type-boolean")) {
            ORBEON.util.Dom.getChildElementByIndex(control, 1).focus();
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-select-appearance-full")) {
            ORBEON.util.Dom.getChildElementByIndex(ORBEON.util.Dom.getChildElementByIndex(control, 0), 0).focus();
        } else if (ORBEON.util.Dom.hasClass(control, "xforms-select1-appearance-xxforms-autocomplete")) {
            ORBEON.util.Dom.getChildElementByIndex(control, 0).focus();
        } else if (typeof control.focus != "undefined") {
            control.focus();
        }

        // Save current value as server value. We usually do this on focus, but for control where we set the focus
        // with xforms:setfocus, we still receive the focus event when the value changes, but after the change event
        // (which means we then don't send the new value to the server).
        if (typeof ORBEON.xforms.Globals.serverValue[controlId] == "undefined") {
            var currentValue = ORBEON.xforms.Controls.getCurrentValue(control);
            ORBEON.xforms.Globals.serverValue[controlId] = currentValue;
        }
    },

    /**
     * Update the xforms-required-empty class as necessary.
     */
    updateRequiredEmpty: function(control) {
        if (ORBEON.util.Dom.hasClass(control, "xforms-required")) {
            if (ORBEON.xforms.Controls.getCurrentValue(control) == "") {
                ORBEON.util.Dom.addClass(control, "xforms-required-empty");
                ORBEON.util.Dom.removeClass(control, "xforms-required-filled");
            } else {
                ORBEON.util.Dom.addClass(control, "xforms-required-filled");
                ORBEON.util.Dom.removeClass(control, "xforms-required-empty");
            }
        } else {
            ORBEON.util.Dom.removeClass(control, "xforms-required-filled");
            ORBEON.util.Dom.removeClass(control, "xforms-required-empty");
        }
    },

    hintActive: function(control, active) {
        var hintLabel = ORBEON.xforms.Events._findHint(control);
        if (hintLabel != null) {
            if (active) {
                // Disable previously active control
                if (ORBEON.xforms.Globals.activeControl != null)
                    ORBEON.xforms.Controls.hintActive(ORBEON.xforms.Globals.activeControl, false);
                ORBEON.xforms.Globals.activeControl = control;
                ORBEON.util.Dom.removeClass(hintLabel, "xforms-hint");
                ORBEON.util.Dom.addClass(hintLabel, "xforms-hint-active");
            } else {
                ORBEON.xforms.Globals.activeControl = null;
                ORBEON.util.Dom.addClass(hintLabel, "xforms-hint");
                ORBEON.util.Dom.removeClass(hintLabel, "xforms-hint-active");
            }
        }
    },

    autosizeTextarea: function(textarea) {
        var scrollHeight = textarea.scrollHeight;
        var clientHeight = textarea.clientHeight;
        var rowHeight = clientHeight / textarea.rows;
        var linesAdded = 0;

        if (scrollHeight > clientHeight) {
            // Grow
            while (scrollHeight > clientHeight) {
                textarea.rows = textarea.rows + 1;
                clientHeight = textarea.clientHeight;
                linesAdded++;

            }
        } else if (scrollHeight < clientHeight) {
            // Shrink
            while (textarea.rows > XFORMS_WIDE_TEXTAREA_MIN_ROWS && scrollHeight < clientHeight - rowHeight) {
                textarea.rows = textarea.rows - 1;
                clientHeight = textarea.clientHeight;
                linesAdded--;
            }
        }
    },

    updateHTMLAreaClasses: function(textarea) {
        var iframe = textarea.previousSibling;
        while (iframe.nodeType != ORBEON.util.Dom.ELEMENT_TYPE) iframe = textarea.previousSibling;
        iframe.className = textarea.className;
    },

    updateLoadingPosition: function(formID) {
        // Compute new X
        var x; {
            var initialRight = ORBEON.xforms.Globals.formLoadingLoadingInitialRightTop[formID][0];
            var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
            x = scrollX + YAHOO.util.Dom.getViewportWidth() - initialRight;
        }
        // Compute new Y
        var y; {
            // Distance between top of viewport and top of the page. Initially 0 when we are at the top of the page.
            var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
            var initialTop = ORBEON.xforms.Globals.formLoadingLoadingInitialRightTop[formID][1];
            y = scrollY + XFORMS_LOADING_MIN_TOP_PADDING > initialTop
                    // Place indicator at a few pixels from the top of the viewport
                    ? scrollY + XFORMS_LOADING_MIN_TOP_PADDING
                    // Loading is visible left at its initial position, so leave it there
                    : initialTop;
        }
        // Position overlay
        var overlay = ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID];
        overlay.cfg.setProperty("x", x);
        overlay.cfg.setProperty("y", y);
    },

    treeOpenSelectedVisible: function(yuiTree, values) {
        for (nodeIndex in yuiTree._nodes) {
            var node = yuiTree._nodes[nodeIndex];
            if (xformsArrayContains(values, node.data.value)) {
                var nodeParent = node.parent;
                while (nodeParent != null) {
                    nodeParent.expand();
                    nodeParent = nodeParent.parent;
                }
            }
        }

    }
};

ORBEON.xforms.Events = {

    /**
     * Look for a hint label that follows the control
     */
    _findHint: function(control) {
        var hintLabel = control;
        while (true) {
            hintLabel = hintLabel.nextSibling;
            // No, we can't found a hint label
            if (hintLabel == null) return null;
            // Yes, we found the hint label
            if (ORBEON.util.Dom.isElement(hintLabel)
                    && (ORBEON.util.Dom.hasClass(hintLabel, "xforms-hint")
                        || ORBEON.util.Dom.hasClass(hintLabel, "xforms-hint-active"))
                    && hintLabel.htmlFor == control.id)
                return hintLabel;
        }
    },

    /**
     * Look for the first parent control which is an XForms control
     */
    _findParentXFormsControl: function(element) {
        while (true) {
            if (!element) return null; // No more parent, stop search
            if (element.xformsElement) {
                // HTML area on Firefox: event target is the document, return the textarea
                return element.xformsElement;
            } else if (element.ownerDocument && element.ownerDocument.xformsElement) {
                // HTML area on IE: event target is the body of the document, return the textarea
                return element.ownerDocument.xformsElement;
            } else if (element.tagName != null
                    && element.tagName.toLowerCase() == "iframe") {
                // This might be the iframe that corresponds to a dialog on IE6
                for (var dialogId in ORBEON.xforms.Globals.dialogs) {
                    var dialog = ORBEON.xforms.Globals.dialogs[dialogId];
                    if (dialog.iframe == element)
                        return dialog.element;
                }
            } else if (element.className != null) {
                if (ORBEON.util.Dom.hasClass(element, "xforms-control")
                        || ORBEON.util.Dom.hasClass(element, "xforms-dialog")
                        || ORBEON.util.Dom.hasClass(element, "xforms-help-image")
                        || ORBEON.util.Dom.hasClass(element, "xforms-alert")) {
                    // We found our XForms element
                    return element;
                }
            }
            // Go to parent and continue search
            element = element.parentNode;
        }
    },

    _keyCodeModifiesField: function(c) {
        return c != 9 && c != 13 && c != 16 && c != 17 && c != 18;
    },

    focus: function(event) {
        if (!ORBEON.xforms.Globals.maskFocusEvents) {
            var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
            if (target != null) {
                // Save id of the control that received blur last
                ORBEON.xforms.Globals.lastFocusControlId = target.id;
                // Activate hint if we found one
                ORBEON.xforms.Controls.hintActive(target, true);
                // Store initial value of control if we don't have a server value already, and if this is is not a list
                // Initial value for lists is set up initialization, as when we receive the focus event the new value is already set.
                if (typeof ORBEON.xforms.Globals.serverValue[target.id] == "undefined"
                        && ! ORBEON.util.Dom.hasClass(target, "xforms-select-appearance-compact")) {
                    ORBEON.xforms.Globals.serverValue[target.id] = target.value;
                }
                // Send focus events
                var previousDOMFocusOut = ORBEON.xforms.Globals.previousDOMFocusOut;
                if (previousDOMFocusOut) {
                    if (previousDOMFocusOut != target) {
                        // HTML area and trees does not throw value change event, so we send the value change to the server
                        // when we get the focus on the next control
                        if (ORBEON.util.Dom.hasClass(previousDOMFocusOut, "xforms-textarea")
                                && ORBEON.util.Dom.hasClass(previousDOMFocusOut, "xforms-mediatype-text-html")) {
                            // To-do: would be nice to use the ORBEON.xforms.Controls.getCurrentValue() so we don't dupplicate the code here
                            var editorInstance = FCKeditorAPI.GetInstance(previousDOMFocusOut.name);
                            previousDOMFocusOut.value = editorInstance.GetXHTML();
                            xformsValueChanged(previousDOMFocusOut, null);
                        } else if (ORBEON.util.Dom.hasClass(previousDOMFocusOut, "xforms-select1-appearance-xxforms-tree")
                                || ORBEON.util.Dom.hasClass(previousDOMFocusOut, "xforms-select-appearance-xxforms-tree")) {
                            xformsValueChanged(previousDOMFocusOut, null);
                        }
                        // Send focus out/focus in events
                        var events = new Array();
                        events.push(xformsCreateEventArray(previousDOMFocusOut, "DOMFocusOut", null));
                        events.push(xformsCreateEventArray(target, "DOMFocusIn", null));
                        xformsFireEvents(events, true);
                    }
                    ORBEON.xforms.Globals.previousDOMFocusOut = null;
                } else {
                    if (document.xformsPreviousDOMFocusIn != target) {
                        xformsFireEvents(new Array(xformsCreateEventArray(target, "DOMFocusIn", null)), true);
                    }
                }
            }
            document.xformsPreviousDOMFocusIn = target;
        } else {
            ORBEON.xforms.Globals.maskFocusEvents = false;
        }
    },

    blur: function(event) {
        if (!ORBEON.xforms.Globals.maskFocusEvents) {
            var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
            if (target != null) {
                // De-activate hint if we found one
                ORBEON.xforms.Controls.hintActive(target, false);
                // This is an event for an XForms control
                ORBEON.xforms.Globals.previousDOMFocusOut = target;
            }
        }
    },

    change: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {
            if (ORBEON.util.Dom.hasClass(target, "xforms-upload")) {
                // For upload controls, generate an xforms-select event when a file is selected
                xformsFireEvents(new Array(xformsCreateEventArray(target, "xforms-select", "")), false);
            } else {
                // When we move out from a field, we don't receive the keyup events corresponding to keypress
                // for that field (go figure!). Se we reset here the count for keypress without keyup for that field.
                if (ORBEON.xforms.Globals.changedIdsRequest[target.id] != null)
                    ORBEON.xforms.Globals.changedIdsRequest[target.id] = 0;

                // For select1 list, make sure we have exactly one value selected
                if (ORBEON.util.Dom.hasClass(target, "xforms-select1-appearance-compact")) {
                    if (target.value == "") {
                        // Stop end-user from deselecting last selected value
                        target.options[0].selected = true;
                        //target.value = target.options[0].value;
                    } else {
                        // Unselect options other than the first one
                        var foundSelected = false;
                        for (var optionIndex = 0; optionIndex < target.options.length; optionIndex++) {
                            var option = target.options[optionIndex];
                            if (option.selected) {
                                if (foundSelected) option.selected = false;
                                else foundSelected = true;
                            }
                        }
                    }
                }

                // Fire change event
                xformsFireEvents([xformsCreateEventArray(target, "xxforms-value-change-with-focus-change",
                    ORBEON.xforms.Controls.getCurrentValue(target))], false);
            }
        }
    },

    keydown: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {
            // Remember that the user is editing this field, so don't overwrite when we receive an event from the server
            // Ignore some key codes that won't modify the value of the field
            if (ORBEON.xforms.Events._keyCodeModifiesField(event.keyCode))
                ORBEON.xforms.Globals.changedIdsRequest[target.id] =
                    ORBEON.xforms.Globals.changedIdsRequest[target.id] == null ? 1
                    : ORBEON.xforms.Globals.changedIdsRequest[target.id] + 1;
        }
    },

    keypress: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {
            // Input field and auto-complete: trigger DOMActive when when enter is pressed
            if (ORBEON.util.Dom.hasClass(target, "xforms-select1-open")
                    || (ORBEON.util.Dom.hasClass(target, "xforms-input")  && !ORBEON.util.Dom.hasClass(target, "xforms-type-boolean"))
                    || ORBEON.util.Dom.hasClass(target, "xforms-secret")) {
                if (event.keyCode == 10 || event.keyCode == 13) {
                    // Prevent default handling of enter, which might be equivalent as a click on some trigger in the form
                    YAHOO.util.Event.preventDefault(event);
                    // Send a value change and DOM activate
                    var events = [
                        xformsCreateEventArray(target, "xxforms-value-change-with-focus-change",
                            ORBEON.xforms.Controls.getCurrentValue(target)),
                        xformsCreateEventArray(target, "DOMActivate", null)
                    ];
                    xformsFireEvents(events, false);
                }
            }
        }
    },

    keyup: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {
            // Save keycode
            if (ORBEON.util.Dom.hasClass(target, "xforms-select1-open"))
                ORBEON.xforms.Globals.autoCompleteLastKeyCode[target.id] = event.keyCode;
            // Remember we have received the keyup for this element
            if (ORBEON.xforms.Events._keyCodeModifiesField(event.keyCode))
                ORBEON.xforms.Globals.changedIdsRequest[target.id]--;
            // Incremental control: treat keypress as a value change event
            if (ORBEON.util.Dom.hasClass(target, "xforms-incremental")) {
                xformsFireEvents([xformsCreateEventArray(target, "xxforms-value-change-with-focus-change",
                    ORBEON.xforms.Controls.getCurrentValue(target))], true);
            }

            // If value is required, add/remove xforms-required-empty appropriately
            ORBEON.xforms.Controls.updateRequiredEmpty(target);

            // Resize wide text area
            if (ORBEON.util.Dom.hasClass(target, "xforms-textarea-appearance-xxforms-autosize")) {
                ORBEON.xforms.Controls.autosizeTextarea(target);
            }
        }
    },

    resize: function(event) {
        for (var i = 0; i < ORBEON.xforms.Globals.autosizeTextareas.length; i++) {
            var textarea = ORBEON.xforms.Globals.autosizeTextareas[i];
            ORBEON.xforms.Controls.autosizeTextarea(textarea);
        }
    },


    _showToolTip: function(event, label, type, message) {
        // Initialize tooltip library
        if (!ORBEON.xforms.Globals.tooltipLibraryInitialized) {
            ORBEON.xforms.Globals.tooltipLibraryInitialized = true;
            tt_init();
        }

        // Figure out if we need to create a div
        // When there is an existing div, check if it has the same message
        var needToCreateDiv;
        var tooltipDivId = label.htmlFor + "-" + type;
        var existingDiv = ORBEON.util.Dom.getElementById(tooltipDivId);
        if (existingDiv != null) {
            if (existingDiv.message == message) {
                needToCreateDiv = false;
            } else {
                needToCreateDiv = true;
                existingDiv.parentNode.removeChild(existingDiv);
            }
        } else {
            needToCreateDiv = true;
        }

        // Create new div when necessary
        if (needToCreateDiv) {
            var divHTML = tt_Htm(this, tooltipDivId, message);
            var container = document.createElement("DIV");
            container.innerHTML = divHTML;
            var newDiv = container.firstChild;
            newDiv.message = message;
            document.body.appendChild(newDiv);
        }

        // Show the help div
        tt_Show(event, tooltipDivId, false, 0, false, false,
            ttOffsetX, ttOffsetY, false, false, ttTemp);
    },

    mouseover: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {
            if (ORBEON.util.Dom.hasClass(target, "xforms-help-image")) {
                // Help image: show help tool-tip
                var label = target.nextSibling;
                while (!ORBEON.util.Dom.isElement(label)) label = target.nextSibling;
                var control = ORBEON.util.Dom.getElementById(label.htmlFor);
                ORBEON.xforms.Events._showToolTip(event, label, "xforms-help", ORBEON.xforms.Controls.getHelpMessage(control));
            } else if (ORBEON.util.Dom.hasClass(target, "xforms-alert-active")) {
                // Alert image: show alert tool-tip
                var control = ORBEON.util.Dom.getElementById(target.htmlFor);
                var message = ORBEON.xforms.Controls.getAlertMessage(control);
                if (message != "") {
                    ORBEON.xforms.Events._showToolTip(event, target, "xforms-alert", ORBEON.xforms.Controls.getAlertMessage(control));
                }
            } else if (ORBEON.util.Dom.hasClass(target, "xforms-dialog-appearance-minimal")) {
                // Minimal dialog: record more is back inside the dialog
                ORBEON.xforms.Globals.dialogMinimalLastMouseOut[target.id] = -1;
            }

            // Check if this control is inside a minimal dialog, in which case we are also inside that dialog
            var current = target;
            while (current != null && current != document) {
                if (ORBEON.util.Dom.hasClass(current, "xforms-dialog-appearance-minimal")) {
                    ORBEON.xforms.Globals.dialogMinimalLastMouseOut[current.id] = -1;
                    break;
                }
                current = current.parentNode;
            }
        }
    },

    mouseout: function(event) {
        var target = ORBEON.xforms.Events._findParentXFormsControl(YAHOO.util.Event.getTarget(event));
        if (target != null) {

            if (ORBEON.util.Dom.hasClass(target, "xforms-help-image")
                    || ORBEON.util.Dom.hasClass(target, "xforms-alert-active")) {
                // Help and alert image: hide tool-tip
                tt_Hide();
            } else if (ORBEON.util.Dom.hasClass(target, "xforms-dialog-appearance-minimal")) {
                // Minimal dialog: register listener to maybe close the dialog
                ORBEON.xforms.Globals.dialogMinimalLastMouseOut[yuiDialog.element.id] = new Date().getTime();
                window.setTimeout(function() { ORBEON.xforms.Events.dialogMinimalCheckMouseIn(yuiDialog); },
                        XFORMS_DELAY_BEFORE_CLOSE_MINIMAL_DIALOG_IN_MS);
            }
        }
    },

    click: function(event) {
        var originalTarget = YAHOO.util.Event.getTarget(event);
        var target = ORBEON.xforms.Events._findParentXFormsControl(originalTarget);

        if (target != null) {
            // Activate hint
            ORBEON.xforms.Controls.hintActive(target, true);

            // Click on output
            if (ORBEON.util.Dom.hasClass(target, "xforms-output")) {
                xformsFireEvents([xformsCreateEventArray(target, "DOMFocusIn", null)], false);
            }

            // Click on trigger
            if ((ORBEON.util.Dom.hasClass(target, "xforms-trigger") || ORBEON.util.Dom.hasClass(target, "xforms-submit"))) {
                YAHOO.util.Event.preventDefault(event);
                if (!ORBEON.util.Dom.hasClass(target, "xforms-readonly")) {
                    // If this is an anchor and we didn't get a chance to register the focus event,
                    // send the focus event here. This is useful for anchors (we don't listen on the
                    // focus event on those, and for buttons on Safari which does not dispatch the focus
                    // event for buttons.
                    ORBEON.xforms.Events.focus(event);
                    xformsFireEvents([xformsCreateEventArray(target, "DOMActivate", null)], false);
                }
            }

            // Click on checkbox or radio button
            if (ORBEON.util.Dom.hasClass(target, "xforms-select1-appearance-full")
                    || ORBEON.util.Dom.hasClass(target, "xforms-select-appearance-full")
                    || ORBEON.util.Dom.hasClass(target, "xforms-input-appearance-full")) {
                xformsFireEvents(new Array(xformsCreateEventArray
                        (target, "xxforms-value-change-with-focus-change",
                                ORBEON.xforms.Controls.getCurrentValue(target), null)), false);
            }

            // Click on calendar inside input field
            if (ORBEON.util.Dom.hasClass(target, "xforms-input") && !ORBEON.util.Dom.hasClass(target, "xforms-type-boolean")) {

                // Initialize calendar when needed
                var displayField = ORBEON.util.Dom.getChildElementByIndex(target, 0);
                var inputField = ORBEON.util.Dom.getChildElementByIndex(target, 1);
                var showCalendar = ORBEON.util.Dom.getChildElementByIndex(target, 2);
                if (ORBEON.util.Dom.hasClass(target, "xforms-type-date")
                        && !ORBEON.util.Dom.hasClass(displayField, "xforms-readonly")) {

                    // Setup calendar library if not done already
                    if (!ORBEON.xforms.Globals.inputCalendarCreated[target.id]) {
                        Calendar.setup({
                            inputField     :    inputField.id,
                            ifFormat       :    "%Y-%m-%d",
                            showsTime      :    false,
                            button         :    target.id,
                            singleClick    :    true,
                            step           :    1,
                            onUpdate       :    ORBEON.xforms.Events.calendarUpdate,
                            onClose        :    ORBEON.xforms.Events.calendarClose,
                            electric       :    true
                        });
                        // JSCalendar sets his listener in the onclick attribute: save it so we can call it later
                        ORBEON.xforms.Globals.inputCalendarOnclick[target.id] = target.onclick;
                        target.onclick = null;
                        ORBEON.xforms.Globals.inputCalendarCreated[target.id] = true;
                        // Save initial value
                        ORBEON.xforms.Globals.inputCalendarCommitedValue[target.id] = ORBEON.xforms.Controls.getCurrentValue(target);
                    }

                    // Event can be received on calendar picker span, or on the containing span
                    ORBEON.xforms.Globals.inputCalendarOnclick[target.id]();
                }
            }

            // Click on remove icon in upload control
            if (ORBEON.util.Dom.hasClass(target, "xforms-upload") && ORBEON.util.Dom.hasClass(originalTarget, "xforms-upload-remove")) {
                xformsFireEvents(new Array(xformsCreateEventArray(target, "xxforms-value-change-with-focus-change", "")), false);
            }

            // Click on menu item
            if (ORBEON.util.Dom.hasClass(target, "xforms-select1-appearance-xxforms-menu")) {

                // Find what is the position in the hiearchy of the item
                var positions = [];
                var currentParent = originalTarget;
                while (true) {
                    if (currentParent.tagName.toLowerCase() == "li") {
                        // Get the position of this li, and add it to positions
                        var liPosition = 0;
                        while (true) {
                            var previousSibling = currentParent.previousSibling;
                            if (previousSibling == null) break;
                            currentParent = previousSibling;
                            if (currentParent.nodeType == ELEMENT_TYPE && currentParent.tagName.toLowerCase() == "li") liPosition++;
                        }
                        positions.push(liPosition);
                    } else if (currentParent.tagName.toLowerCase() == "div" && ORBEON.util.Dom.hasClass(currentParent, "yuimenubar")) {
                        // Got to the top of the tree
                        break;
                    }
                    currentParent = currentParent.parentNode;
                }
                positions = positions.reverse();

                // Find value for this item
                var currentLevel = ORBEON.xforms.Globals.menuItemsets[target.id];
                var increment = 0;
                for (var positionIndex = 0; positionIndex < positions.length; positionIndex++) {
                    var position = positions[positionIndex];
                    currentLevel = currentLevel[position + increment];
                    increment = 3;
                }

                // Send value change to server
                var itemValue = currentLevel[1];
                xformsFireEvents(new Array(xformsCreateEventArray(target, "xxforms-value-change-with-focus-change", itemValue)), false);
                // Close the menu
                ORBEON.xforms.Globals.menuYui[target.id].clearActiveItem();
            }
        }
    },

    /**
     * Upon scrolling or resizing, adjust position of loading indicators
     */
    scrollOrResize: function() {
        for (var formID in ORBEON.xforms.Globals.formLoadingLoadingOverlay) {
            var overlay = ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID];
            if (overlay && overlay.cfg.getProperty("visible"))
                ORBEON.xforms.Controls.updateLoadingPosition(formID);
        }
    },

    /**
     * Send notification to XForms engine end-user clicked on day.
     */
    calendarUpdate: function(calendar) {
        if (ORBEON.util.Dom.hasClass(calendar.activeDiv, "day")) {
            var inputField = calendar.params.inputField;
            var element = inputField.parentNode;
            var newValue = ORBEON.xforms.Controls.getCurrentValue(element);
            ORBEON.xforms.Globals.inputCalendarCommitedValue[element.id] = newValue;
            xformsFireEvents([xformsCreateEventArray(element, "xxforms-value-change-with-focus-change", newValue)], false);
        }
    },

    /**
     * Restore last value actualy selected by user in the input field, which at this point could
     * contain a value the user just browsed to, without selecting it.
     */
    calendarClose: function(calendar) {
        var inputField = calendar.params.inputField;
        var element = inputField.parentNode;
        inputField.value = ORBEON.xforms.Globals.inputCalendarCommitedValue[element.id];
        calendar.hide();
    },

    sliderValueChange: function(offset) {
        // Notify server that value changed
        var rangeControl = ORBEON.util.Dom.getElementById(this.id);
        rangeControl.value = offset / 200;
        xformsValueChanged(rangeControl, null);
    },

    /**
     * Called by the YUI menu library when a click happens a menu entry.
     */
    menuClick: function (eventType, arguments, userObject) {
        var menu = userObject["menu"];
        var value = userObject["value"];
        xformsFireEvents([xformsCreateEventArray(menu, "xxforms-value-change-with-focus-change", value)], false);
    },

    /**
     * Event listener on dialogs called by YUI when the dialog is closed. If the dialog was closed by the user (not
     * because the server told use to close the dialog), then we want to notify the server that this happened.
     */
    dialogClose: function(type, args, me) {
        var dialogId = me;
        var dialog = ORBEON.util.Dom.getElementById(dialogId);
        xformsFireEvents([xformsCreateEventArray(dialog, "xxforms-dialog-close")], false);
    },

    /**
     * What we need to do when there is a click on a tree (select and select1)
     */
    treeClickFocus: function(control) {
        var isIncremental = ORBEON.util.Dom.hasClass(control, "xforms-incremental");
        if (ORBEON.xforms.Globals.lastFocusControlId != control.id) {
            // We are comming from another control, simulate a focus on this control
            var focusEvent = { target: control };
            ORBEON.xforms.Events.focus(focusEvent);
        }
        // Preemptively store current control in previousDOMFocusOut, so when another control gets
        // the focus it will send the value of this control to the server
        ORBEON.xforms.Globals.previousDOMFocusOut = control;
    },

    treeClickValueUpdated: function(control) {
        // If we are in incremental mode, send value to the server on every click
        if (ORBEON.util.Dom.hasClass(control, "xforms-incremental"))
            xformsValueChanged(control);
    },

    /**
     * xforms:select tree: handle click on check box
     */
    treeCheckClick: function() {
        var tree = this.tree;
        var control = ORBEON.util.Dom.getElementById(tree.id);
        ORBEON.xforms.Events.treeClickFocus(control);
        control.value = "";
        for (nodeIndex in tree._nodes) {
            var node = tree._nodes[nodeIndex];
            if (node.checkState == 2) {
                if (control.value != "") control.value += " ";
                control.value += node.data.value;
            }
        }
        ORBEON.xforms.Events.treeClickValueUpdated(control);
    },

    /**
     * xforms:select and xforms:select tree: handle click on label
     */
    treeLabelClick: function(node) {
        var yuiTree = this;
        var control = document.getElementById(yuiTree.id);
        var allowMultipleSelection = ORBEON.util.Dom.hasClass(control, "xforms-select");
        if (allowMultipleSelection) {
            // If checked uncheck, if unchecked check
            if (node.checked) {
                node.uncheck();
            } else {
                node.check();
            }
            // Call listener on check event
            node.onCheckClick();
        } else {
            // Unselect the old node and select the new node
            var oldNode = yuiTree.getNodeByProperty("value", control.value);
            if (oldNode != null)
                YAHOO.util.Dom.removeClass(oldNode.getLabelEl(), "xforms-tree-label-selected");
            if (node != null)
                YAHOO.util.Dom.addClass(node.getLabelEl(), "xforms-tree-label-selected");
            // Make we know this control has the focus
            ORBEON.xforms.Events.treeClickFocus(control);
            // Store the new value for this control
            control.value = node.data.value;
            // Send new value to server
            ORBEON.xforms.Events.treeClickValueUpdated(control);
        }
    },

    /**
     * Called when end-users click on the show/hide details link in the error panel.
     */
    errorShowHideDetails: function() {
        var errorBodyDiv = this.parentNode.parentNode.parentNode;
        var detailsHidden = ORBEON.util.Dom.getChildElementByClass(errorBodyDiv, "xforms-error-panel-details-hidden");
        var detailsShown = ORBEON.util.Dom.getChildElementByClass(errorBodyDiv, "xforms-error-panel-details-shown");
        if (this.className == "xforms-error-panel-show-details") {
            ORBEON.util.Dom.addClass(detailsHidden, "xforms-disabled");
            ORBEON.util.Dom.removeClass(detailsShown, "xforms-disabled");
        } else {
            ORBEON.util.Dom.removeClass(detailsHidden, "xforms-disabled");
            ORBEON.util.Dom.addClass(detailsShown, "xforms-disabled");
        }
    },

    /**
     * When the error dialog is closed, we make sure that the "details" section is closed,
     * so it will be closed the next time the dialog is opened.
     */
    errorPanelClosed: function(type, args, formID) {
        var errorPanel = ORBEON.xforms.Globals.formErrorPanel[formID];
        var errorBodyDiv = errorPanel.errorDetailsDiv.parentNode.parentNode;
        var detailsHidden = ORBEON.util.Dom.getChildElementByClass(errorBodyDiv, "xforms-error-panel-details-hidden");
        var detailsShown = ORBEON.util.Dom.getChildElementByClass(errorBodyDiv, "xforms-error-panel-details-shown");
        ORBEON.util.Dom.removeClass(detailsHidden, "xforms-disabled");
        ORBEON.util.Dom.addClass(detailsShown, "xforms-disabled");
    },

    errorCloseClicked: function(event, errorPanel) {
        errorPanel.hide();
    },

    /**
     * Called for each minimal dialog when there is a click on the document.
     * We have one listener per dialog, which listens to those events all the time,
     * not just when the dialog is open.
     */
    dialogMinimalBodyClick: function(event, yuiDialog) {
        // If this dialog is visible
        if (ORBEON.xforms.Globals.dialogMinimalVisible[yuiDialog.element.id]) {
            // Abord if one of the parents is drop-down dialog
            var current = YAHOO.util.Event.getTarget(event);
            var foundDropDownParent = false;
            while (current != null && current != document) {
                if (ORBEON.util.Dom.hasClass(current, "xforms-dialog-appearance-minimal")) {
                    foundDropDownParent = true;
                    break;
                }
                current = current.parentNode;
            }
            if (!foundDropDownParent)
                xformsFireEvents([xformsCreateEventArray(yuiDialog.element, "xxforms-dialog-close")], false);
        }
    },

    /**
     * Called when the mouse is outside of a minimal dialog for more than a certain amount of time.
     * Here we close the dialog if appropriate.
     */
    dialogMinimalCheckMouseIn: function(yuiDialog) {
        var current = new Date().getTime();
        if (ORBEON.xforms.Globals.dialogMinimalVisible[yuiDialog.element.id]
                && ORBEON.xforms.Globals.dialogMinimalLastMouseOut[yuiDialog.element.id] != -1
                && current - ORBEON.xforms.Globals.dialogMinimalLastMouseOut[yuiDialog.element.id] >= XFORMS_DELAY_BEFORE_CLOSE_MINIMAL_DIALOG_IN_MS) {
            xformsFireEvents([xformsCreateEventArray(yuiDialog.element, "xxforms-dialog-close")], false);
        }
    }
};

ORBEON.xforms.Init = {

    /**
     * Functions used to initialize special controls
     */
    _specialControlsInitFunctions: null,
    _getSpecialControlsInitFunctions: function () {
        ORBEON.xforms.Init._specialControlsInitFunctions = ORBEON.xforms.Init._specialControlsInitFunctions || {
            "select1": {
                "compact" : ORBEON.xforms.Init._list,
                "{http://orbeon.org/oxf/xml/xforms}autocomplete": ORBEON.xforms.Init._autoComplete,
                "{http://orbeon.org/oxf/xml/xforms}menu": ORBEON.xforms.Init._menu,
                "{http://orbeon.org/oxf/xml/xforms}tree": ORBEON.xforms.Init._tree
            },
            "select": {
                "compact" : ORBEON.xforms.Init._list,
                "{http://orbeon.org/oxf/xml/xforms}tree": ORBEON.xforms.Init._tree
            },
            "range": { "": ORBEON.xforms.Init._range },
            "textarea": {
                "{http://orbeon.org/oxf/xml/xforms}autosize": ORBEON.xforms.Init._widetextArea,
                "text/html": ORBEON.xforms.Init._htmlArea
            },
            "dialog": {
                "": ORBEON.xforms.Init._dialog,
                "full": ORBEON.xforms.Init._dialog,
                "minimal": ORBEON.xforms.Init._dialog 
            }
        };
        return ORBEON.xforms.Init._specialControlsInitFunctions;
    },

    registerListenersOnFormElements: function() {
        for (var i = 0; i < document.forms.length; i++) {
            var form = document.forms[i];
            if (ORBEON.util.Dom.hasClass(form, "xforms-form")) {
                for (var j = 0; j < form.elements.length; j++) {
                    var element = form.elements[j];
                    ORBEON.xforms.Init.registerListenersOnFormElement((element));
                }
            }
        }
    },

    registerListenersOnFormElement: function(element) {
        YAHOO.util.Event.addListener(element, "focus", ORBEON.xforms.Events.focus);
        YAHOO.util.Event.addListener(element, "blur", ORBEON.xforms.Events.blur);
        YAHOO.util.Event.addListener(element, "change", ORBEON.xforms.Events.change);
    },

    document: function() {

        // Register events in the capture phase for W3C-compliant browsers.
        if (ORBEON.xforms.Globals.supportsCaptureEvents) {
            window.addEventListener("focus", ORBEON.xforms.Events.focus, true);
            window.addEventListener("blur", ORBEON.xforms.Events.blur, true);
            window.addEventListener("change", ORBEON.xforms.Events.change, true);
        } else {
            ORBEON.xforms.Init.registerListenersOnFormElements();
        }

        // Register events that bubble on document for all browsers
        YAHOO.util.Event.addListener(document, "keypress", ORBEON.xforms.Events.keypress);
        YAHOO.util.Event.addListener(document, "keydown", ORBEON.xforms.Events.keydown);
        YAHOO.util.Event.addListener(document, "keyup", ORBEON.xforms.Events.keyup);
        YAHOO.util.Event.addListener(document, "mouseover", ORBEON.xforms.Events.mouseover);
        YAHOO.util.Event.addListener(document, "mouseout", ORBEON.xforms.Events.mouseout);
        YAHOO.util.Event.addListener(document, "click", ORBEON.xforms.Events.click);
        YAHOO.util.Event.addListener(window, "resize", ORBEON.xforms.Events.resize);
        YAHOO.widget.Overlay.windowScrollEvent.subscribe(ORBEON.xforms.Events.scrollOrResize);
        YAHOO.widget.Overlay.windowResizeEvent.subscribe(ORBEON.xforms.Events.scrollOrResize);

        // Initialize XForms server URL
        // NOTE: The server provides us with a base URL, but we must use a client-side value to support proxying
        var scripts = document.getElementsByTagName("script");
        for (var scriptIndex = 0; scriptIndex < scripts.length; scriptIndex++) {
            var script = scripts[scriptIndex];
            var scriptSrc = ORBEON.util.Dom.getAttribute(script, "src");
            if (scriptSrc != null) {
                var startPathToJavaScript = scriptSrc.indexOf(PATH_TO_JAVASCRIPT_1);
                if (startPathToJavaScript == -1)
                    startPathToJavaScript = scriptSrc.indexOf(PATH_TO_JAVASCRIPT_2);
                if (startPathToJavaScript != -1) {
                    BASE_URL = scriptSrc.substr(0, startPathToJavaScript);
                    XFORMS_SERVER_URL = BASE_URL + "/xforms-server";
                    break;
                }
            }
        }

        // Override image location for YUI to use local images
        var yuiBaseURL = BASE_URL + "/ops/images/yui/";
        if (YAHOO && YAHOO.widget) {
            if (YAHOO.widget.Module) {
                YAHOO.widget.Module.IMG_ROOT = yuiBaseURL;
                YAHOO.widget.Module.IMG_ROOT_SSL = yuiBaseURL;
            }
            if (YAHOO.widget.Calendar_Core) {
                YAHOO.widget.Calendar_Core.IMG_ROOT = yuiBaseURL;
                YAHOO.widget.Calendar_Core.IMG_ROOT_SSL = yuiBaseURL;
            }
            if (YAHOO.widget.MenuModuleItem) {
                YAHOO.widget.MenuModuleItem.prototype.IMG_ROOT = yuiBaseURL;
                YAHOO.widget.MenuModuleItem.prototype.IMG_ROOT_SSL = yuiBaseURL;
            }
        }

        // Initialize special controls
        if (!(window.opsXFormsControls === undefined)) {
            var initFunctions = ORBEON.xforms.Init._getSpecialControlsInitFunctions();
            // Iterate over controls
            for (var controlType in window.opsXFormsControls["controls"]) {
                if (initFunctions[controlType]) {
                    var controlAppearances = window.opsXFormsControls["controls"][controlType];
                    // Iterate over appearance for current control
                    for (var controlAppearance in controlAppearances) {
                        var initFunction = initFunctions[controlType][controlAppearance];
                        if (initFunction) {
                            var controlIds = controlAppearances[controlAppearance];
                            // Iterate over controls
                            for (var controlIndex = 0; controlIndex < controlIds.length; controlIndex++) {
                                var control = ORBEON.util.Dom.getElementById(controlIds[controlIndex]);
                                initFunction(control);
                            }
                        }
                    }
                }
            }
        }

        // Initialize attributes on form
        for (var formIndex = 0; formIndex < document.forms.length; formIndex++) {
            var form = document.forms[formIndex];
            // If this is an XForms form, procede with initialization
            if (ORBEON.util.Dom.hasClass(form, "xforms-form")) {
                var formID = document.forms[formIndex].id;

                // Initialize loading and error indicator
                ORBEON.xforms.Globals.formErrorPanel[formID] = null;
                ORBEON.xforms.Globals.formLoadingNone[formID] = null;

                var xformsLoadingCount = 0;
                for (var formChildIndex = 0; formChildIndex < form.childNodes.length; formChildIndex++) {
                    if (xformsLoadingCount == 3) break;
                    var formChild = form.childNodes[formChildIndex];
                    if (formChild.className == "xforms-loading-loading") {
                        formChild.style.display = "block";
                        ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID] = new YAHOO.widget.Overlay(formChild);
                        ORBEON.xforms.Globals.formLoadingLoadingInitialRightTop[formID] = [
                            YAHOO.util.Dom.getViewportWidth() - YAHOO.util.Dom.getX(formChild),
                            YAHOO.util.Dom.getY(formChild)
                        ];
                        formChild.style.right = "auto";
                        ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID].cfg.setProperty("visible", false);
                        xformsLoadingCount++;
                        continue;
                    }
                    if (ORBEON.util.Dom.isElement(formChild) && ORBEON.util.Dom.hasClass(formChild, "xforms-error-panel")) {

                        // Create and store error panel
                        YAHOO.util.Dom.generateId(formChild);
                        ORBEON.util.Dom.removeClass(formChild, "xforms-initially-hidden");
                        var errorPanel = new YAHOO.widget.Panel(formChild.id, {
                            width: "700px",
                            modal: true,
                            fixedcenter: false,
                            underlay: "shadow",
                            visible: false,
                            constraintoviewport: true,
                            draggable: true
                        });
                        errorPanel.render();
                        errorPanel.element.style.display = "none";
                        errorPanel.beforeHideEvent.subscribe(ORBEON.xforms.Events.errorPanelClosed, formID);
                        ORBEON.xforms.Globals.formErrorPanel[formID] = errorPanel;

                        // Find reference to elements in the deails hidden section
                        var bodyDiv = ORBEON.util.Dom.getChildElementByClass(formChild, "bd");
                        var detailsHiddenDiv = ORBEON.util.Dom.getChildElementByClass(bodyDiv, "xforms-error-panel-details-hidden");
                        var showDetailsA = ORBEON.util.Dom.getChildElementByIndex(ORBEON.util.Dom.getChildElementByIndex(detailsHiddenDiv, 0), 0);
                        YAHOO.util.Dom.generateId(showDetailsA);

                        // Find reference to elements in the deails shown section
                        var detailsShownDiv = ORBEON.util.Dom.getChildElementByClass(bodyDiv, "xforms-error-panel-details-shown");
                        var hideDetailsA = ORBEON.util.Dom.getChildElementByIndex(ORBEON.util.Dom.getChildElementByIndex(detailsShownDiv, 0), 0);
                        YAHOO.util.Dom.generateId(hideDetailsA);
                        errorPanel.errorDetailsDiv = ORBEON.util.Dom.getChildElementByClass(detailsShownDiv, "xforms-error-panel-details");

                        // Register listener that will show/hide the detail section
                        YAHOO.util.Event.addListener(showDetailsA.id, "click", ORBEON.xforms.Events.errorShowHideDetails);
                        YAHOO.util.Event.addListener(hideDetailsA.id, "click", ORBEON.xforms.Events.errorShowHideDetails);

                        //
                        var closeA = YAHOO.util.Dom.getElementsByClassName("xforms-error-panel-close", null, formChild);
                        if (closeA.length != 0) {
                            YAHOO.util.Dom.generateId(closeA[0]);
                            YAHOO.util.Event.addListener(closeA[0].id, "click", ORBEON.xforms.Events.errorCloseClicked, errorPanel);
                        }

                        xformsLoadingCount++;
                        continue;
                    }
                    if (formChild.className == "xforms-loading-none") {
                        ORBEON.xforms.Globals.formLoadingNone[formID] = formChild;
                        xformsLoadingCount++;
                        continue;
                    }
                }

                var elements = form.elements;
                var xformsRepeatTree;
                var xformsRepeatIndices;
                for (var elementIndex = 0; elementIndex < elements.length; elementIndex++) {
                    var element = elements[elementIndex];
                    if (element.name.indexOf("$static-state") != -1) {
                        ORBEON.xforms.Globals.formStaticState[formID] = element;
                    } else if (element.name.indexOf("$dynamic-state") != -1) {
                        ORBEON.xforms.Globals.formDynamicState[formID] = element;
                    } else if (element.name.indexOf("$server-events") != -1) {
                        ORBEON.xforms.Globals.formServerEvents[formID] = element;
                    } else if (element.name.indexOf("$client-state") != -1) {
                        ORBEON.xforms.Globals.formClientState[formID] = element;
                        if (element.value == "")
                            xformsStoreInClientState(formID, "ajax-dynamic-state",
                                    ORBEON.xforms.Globals.formDynamicState[formID].value);
                    } else if (element.name.indexOf("$repeat-tree") != -1) {
                        xformsRepeatTree = element;
                    } else if (element.name.indexOf("$repeat-indexes") != -1) {
                        xformsRepeatIndices = element;
                        // This is the last input field we are interested in
                        break;
                    }
                }

                // Parse and store initial repeat hierarchy
                var repeatTreeString = xformsRepeatTree.value;
                var repeatTree = repeatTreeString.split(",");
                for (var repeatIndex = 0; repeatIndex < repeatTree.length; repeatIndex++) {
                    var repeatInfo = repeatTree[repeatIndex].split(" ");
                    var id = repeatInfo[0];
                    var parent = repeatInfo.length > 1 ? repeatInfo[repeatInfo.length - 1] : null;
                    ORBEON.xforms.Globals.repeatTreeChildToParent[id] = parent;
                }
                for (var child in ORBEON.xforms.Globals.repeatTreeChildToParent) {
                    var parent = ORBEON.xforms.Globals.repeatTreeChildToParent[child];
                    while (parent != null) {
                        if (!ORBEON.xforms.Globals.repeatTreeParentToAllChildren[parent])
                            ORBEON.xforms.Globals.repeatTreeParentToAllChildren[parent] = new Array();
                        ORBEON.xforms.Globals.repeatTreeParentToAllChildren[parent].push(child);
                        parent = ORBEON.xforms.Globals.repeatTreeChildToParent[parent];
                    }
                }

                // Parse and store initial repeat indexes
                var repeatIndexesString = xformsRepeatIndices.value;
                var repeatIndexes = repeatIndexesString.split(",");
                for (var repeatIndex = 0; repeatIndex < repeatIndexes.length; repeatIndex++) {
                    var repeatInfo = repeatIndexes[repeatIndex].split(" ");
                    var id = repeatInfo[0];
                    var index = repeatInfo[repeatInfo.length - 1];
                    ORBEON.xforms.Globals.repeatIndexes[id] = index;
                }

                // Ask server to resend events if this is not the first time load is called
                if (xformsGetFromClientState(formID, "load-did-run") == null) {
                    xformsStoreInClientState(formID, "load-did-run", "true");
                } else {
                    xformsFireEvents(new Array(xformsCreateEventArray(form, "xxforms-all-events-required", null, null)), false);
                }
            }
        }

        // Run code sent by server
        if (typeof xformsPageLoadedServer != "undefined" && !ORBEON.xforms.Globals.fckEditorLoading)
            xformsPageLoadedServer();
    },


    /**
     * Initialize a newly copied subtree.
     *
     * Some of the more advanced controls are initialized when the page first loads. The server sets the value of the
     * opsXFormsControls variable to tell the client the id of those controls and the type of each control. When new
     * controls are added, this function must be called so those the inserted advanced controls are initialized as
     * well.
     */
    insertedElement: function(element) {
        if (element.nodeType == ORBEON.util.Dom.ELEMENT_TYPE) {
            if (ORBEON.util.Dom.hasClass(element, "xforms-select1-appearance-xxforms-autocomplete")) {
                ORBEON.xforms.Init._autoComplete(element);
            }
            for (var childIndex = 0; childIndex < element.childNodes.length; childIndex++) {
                var child = element.childNodes[childIndex];
                if (child.nodeType == ORBEON.util.Dom.ELEMENT_TYPE)
                    ORBEON.xforms.Init.insertedElement(child);
            }
        }
    },

    _autoComplete: function(autoComplete) {
        var textfield = ORBEON.util.Dom.getChildElementByIndex(autoComplete, 0);
        var select = ORBEON.util.Dom.getChildElementByIndex(autoComplete, 1);
        // Get list of possible values from the select
        var values = new Array();
        for (var optionIndex = 1; optionIndex < select.options.length; optionIndex++)
            values.push(select.options[optionIndex].value);
        // Initialize auto-complete input
        var noFilter = ORBEON.util.Dom.hasClass(autoComplete, "xforms-select1-open-autocomplete-nofilter");
        ORBEON.xforms.Globals.autoCompleteOpen[autoComplete.id] = actb(textfield, values, noFilter);
    },

    _widetextArea: function(textarea) {
        ORBEON.xforms.Globals.autosizeTextareas.push(textarea);
        ORBEON.xforms.Controls.autosizeTextarea(textarea);
    },

    _range: function(range) {
        range.tabIndex = 0;
        range.previousValue = 0; // Will be modified once the initial value can be set
        var thumbDiv = range.firstChild;
        if (thumbDiv.nodeType != ELEMENT_TYPE) thumbDiv = thumbDiv.nextSibling;
        thumbDiv.id = range.id + XFORMS_SEPARATOR_1 + "thumb";
        var slider = YAHOO.widget.Slider.getHorizSlider(range.id, thumbDiv.id, 0, 200);
        slider.subscribe("change", ORBEON.xforms.Events.sliderValueChange);
    },

    _addToTree: function (treeDiv, nameValueArray, treeNode, firstPosition) {
        for (var arrayIndex = firstPosition; arrayIndex < nameValueArray.length; arrayIndex++) {
            // Extract information from the first 3 position in the array
            var childArray = nameValueArray[arrayIndex];
            var name = childArray[0];
            var value = childArray[1];
            var selected = childArray[2];
            var hasSelected = typeof selected == "boolean";
            // Create node and add to tree
            var nodeInformation = { label: name, value: value };
            var childNode;
            if (treeDiv.xformsAllowMultipleSelection) {
                childNode = new YAHOO.widget.TaskNode(nodeInformation, treeNode, false);
                childNode.onCheckClick = ORBEON.xforms.Events.treeCheckClick;
            } else {
                childNode = new YAHOO.widget.TextNode(nodeInformation, treeNode, false);
            }
            ORBEON.xforms.Init._addToTree(treeDiv, childArray, childNode, hasSelected ? 3 : 2);
            // Add this value to the list if selected
            if (hasSelected && selected) {
                if (treeDiv.value != "") treeDiv.value += " ";
                treeDiv.value += value;
            }
        }
    },

    _initTreeDivFromArray: function(treeDiv, yuiTree, treeArray) {
        // Populate the tree
        var treeRoot = yuiTree.getRoot();
        ORBEON.xforms.Init._addToTree(treeDiv, treeArray, treeRoot, 0);
        // For select tree, check the node that are selected
        if (treeDiv.xformsAllowMultipleSelection) {
            var values = treeDiv.value.split(" ");
            var nodes = yuiTree.getNodesByProperty();
            // nodes can be null when the tree is empty
            if (nodes != null) {
                for (var nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
                    var node = nodes[nodeIndex];
                    var currentValue = node.data.value;
                    for (var valueIndex = 0; valueIndex < values.length; valueIndex++) {
                        if (currentValue == values[valueIndex]) {
                            node.check()
                            break;
                        }
                    }
                }
            }
        }
        // Make selected nodes visible
        var values = treeDiv.xformsAllowMultipleSelection ? treeDiv.value.split(" ") : [ treeDiv.value ];
        ORBEON.xforms.Controls.treeOpenSelectedVisible(yuiTree, values);
        // Draw the tree the first time
        yuiTree.draw();
    },

    _tree: function(treeDiv) {

        // Save in the control if it allows multiple selection
        treeDiv.xformsAllowMultipleSelection = ORBEON.util.Dom.hasClass(treeDiv, "xforms-select");
        // Parse data put by the server in the div
        var treeString = ORBEON.util.Dom.getStringValue(treeDiv);
        var treeArray = ORBEON.util.String.eval(treeString);
        ORBEON.util.Dom.setStringValue(treeDiv, "");
        treeDiv.value = "";
        // Create YUI tree and save a copy
        var yuiTree = new YAHOO.widget.TreeView(treeDiv.id);
        ORBEON.xforms.Globals.treeYui[treeDiv.id] = yuiTree;
        // Build the tree
        ORBEON.xforms.Init._initTreeDivFromArray(treeDiv, yuiTree, treeArray);
        // Save value in tree
        treeDiv.previousValue = treeDiv.value;
        // Show the currently selected value
        if (!treeDiv.xformsAllowMultipleSelection) {
            var selectedNode = yuiTree.getNodeByProperty("value", treeDiv.value);
            // Handle cases where the current value is not in the tree. In most cases this is because the value is
            // empty string; no value has been selected yet.
            if (selectedNode != null)
                YAHOO.util.Dom.addClass(selectedNode.getLabelEl(), "xforms-tree-label-selected");
        }
        // Register event handler for click on label
        yuiTree.subscribe("labelClick", ORBEON.xforms.Events.treeLabelClick);
        ORBEON.util.Dom.removeClass(treeDiv, "xforms-initially-hidden");
    },

    /**
     * Create a sub-menu attached to the given menu item. In the nameValueArray we
     * ignore the first 3 items that correspond to the menuItem.
     */
    _addToMenuItem: function (menu, nameValueArray, menuItem) {
        // Assign id to menu item
        if (menuItem.element.id == "")
            YAHOO.util.Dom.generateId(menuItem.element);
        // Handle click on menu item
        menuItem.clickEvent.subscribe(ORBEON.xforms.Events.menuClick,
            {"menu": menu, "value": nameValueArray[1]});
        // Create sub-menu if necessary
        if (nameValueArray.length > 3) {
            // Create submenu
            var subMenu = new YAHOO.widget.Menu(menuItem.element.id + "menu");
            // Add menu items to submenu
            for (var arrayIndex = 3; arrayIndex < nameValueArray.length; arrayIndex++) {
                // Extract information from the first 3 position in the array
                var childArray = nameValueArray[arrayIndex];
                var name = childArray[0];
                var value = childArray[1];
                var selected = childArray[2];
                // Create menu item and add to menu
                var subMenuItem = new YAHOO.widget.MenuItem(name);
                subMenu.addItem(subMenuItem);
                // Add sub-sub menu
                ORBEON.xforms.Init._addToMenuItem(menu, childArray, subMenuItem)
            }
            menuItem.cfg.setProperty("submenu", subMenu);
        }
    },

    _menu: function (menu) {
        // Find the divs for the YUI menu and for the values inside the control
        var yuiMenuDiv;
        var valuesDiv;
        for (var j = 0; j < menu.childNodes.length; j++) {
            var childNode =  menu.childNodes[j];
            if (childNode.nodeType == ELEMENT_TYPE) {
                if (ORBEON.util.Dom.hasClass(childNode, "yuimenubar")) {
                    yuiMenuDiv = childNode;
                } else if (ORBEON.util.Dom.hasClass(childNode, "xforms-initially-hidden")) {
                    valuesDiv = childNode;
                }
            }
        }

        // Extract menu hierarchy from HTML
        var menuString = ORBEON.util.Dom.getStringValue(valuesDiv);
        ORBEON.xforms.Globals.menuItemsets[menu.id] = ORBEON.util.String.eval(menuString);

        // Initialize tree
        YAHOO.util.Dom.generateId(yuiMenuDiv);
        var yuiMenu = new YAHOO.widget.MenuBar(yuiMenuDiv.id, {
            autosubmenudisplay: true,
            hidedelay: 750,
            lazyload: true
        });
        yuiMenu.render();
        ORBEON.xforms.Globals.menuYui[menu.id] = yuiMenu;
    },

    /**
     * Initialize HTML areas.
     */
    _htmlArea: function (htmlArea) {
        var fckEditor = new FCKeditor(htmlArea.name);
        if (!xformsArrayContains(ORBEON.xforms.Globals.htmlAreaNames, htmlArea.name))
            ORBEON.xforms.Globals.htmlAreaNames.push(htmlArea.name);
        fckEditor.BasePath = BASE_URL + "/ops/fckeditor/";
        fckEditor.ToolbarSet = "OPS";

        // Change the language of the FCK Editor for its spellchecker, based on the USER_LANGUAGE variable
        var type_check = typeof USER_LANGUAGE;
        if (type_check != 'undefined') {
	        fckEditor.Config["AutoDetectLanguage"] = false;
    	    fckEditor.Config["DefaultLanguage"] = (USER_LANGUAGE != '') ? USER_LANGUAGE : 'en';
    	}
        // Change the path to a custom configuration, based on the FCK_CUSTOM_CONFIG variable
    	type_check = typeof FCK_CUSTOM_CONFIG;
    	if (type_check != 'undefined')
    		fckEditor.Config["CustomConfigurationsPath"] = FCK_CUSTOM_CONFIG;

        if (ORBEON.xforms.Globals.fckEditorLoading) {
            ORBEON.xforms.Globals.fckEditorsToLoad.push(fckEditor);
        } else {
            ORBEON.xforms.Globals.fckEditorLoading = true;
            fckEditor.ReplaceTextarea();
            ORBEON.xforms.Controls.updateHTMLAreaClasses(ORBEON.util.Dom.getElementById(fckEditor.InstanceName));
        }
    },

    /**
     * For all the controls except list, we figure out the initial value of the control when
     * receiving the first focus event. For the lists on Firefox, the value has already changed
     * when we receive the focus event. So here we save the value for lists when the page loads.
     */
    _list: function(list) {
        var value = "";
        for (var i = 0; i < list.options.length; i++) {
            var option = list.options[i];
            if (option.selected) {
                if (value != "") value += " ";
                value += option.value;
            }
        }
        ORBEON.xforms.Globals.serverValue[list.id] = value;
    },

    /**
     * Initialize dialogs
     */
    _dialog: function(dialog) {
        var isModal = ORBEON.util.Dom.hasClass(dialog, "xforms-dialog-modal");
        var hasClose = ORBEON.util.Dom.hasClass(dialog, "xforms-dialog-close-true");
        var draggable = ORBEON.util.Dom.hasClass(dialog, "xforms-dialog-draggable-true");
        var isMinimal = ORBEON.util.Dom.hasClass(dialog, "xforms-dialog-appearance-minimal");
        ORBEON.util.Dom.removeClass(dialog, "xforms-initially-hidden");

        // Create dialog object
        if (isMinimal) {
            // Create minimal dialog
            yuiDialog = new YAHOO.widget.Overlay(dialog.id, {
                visible: false,
                constraintoviewport: true,
                iframe: true
            });
            // Close the dialog when users click on document
            YAHOO.util.Event.addListener(document.body, "click", ORBEON.xforms.Events.dialogMinimalBodyClick, yuiDialog);
        } else {
            // Create full dialog
            yuiDialog = new YAHOO.widget.Dialog(dialog.id, {
                modal: isModal,
                close: hasClose,
                visible: false,
                draggable: draggable,
                fixedcenter: false,
                constraintoviewport: true,
                underlay: "shadow"
            });
            // Register listener for when the dialog is closed by a click on the "x"
            yuiDialog.beforeHideEvent.subscribe(ORBEON.xforms.Events.dialogClose, dialog.id);
        }
        yuiDialog.render();

        // We hide the dialog as it otherwise interfers with other dialogs, preventing
        // the cursor from showing in input fields of other dialogs
        yuiDialog.element.style.display = "none";
        ORBEON.xforms.Globals.dialogs[dialog.id] = yuiDialog;
    }
};

ORBEON.xforms.Server = {

    Event: function(form, targetId, otherId, value, eventName, bubbles, cancelable, ignoreErrors) {
        this.form = form;
        this.targetId = targetId;
        this.otherId = otherId;
        this.value = value;
        this.eventName = eventName;
        this.bubbles = bubbles;
        this.cancelable = cancelable;
        this.ignoreErrors = ignoreErrors;
    },

    /**
     * When an exception happens while we communicate with the server, we catch it and show an error in the UI.
     * This is to prevent the UI from becoming totally unusable after an error.
     */
    exceptionWhenTalkingToServer: function(e, formID) {
        ORBEON.util.Utils.logException("JavaScript error", e);
        var details = "Exception in client-side code.";
        details += "<ul>";
        if (e.message != null) details += "<li>Message: " + e.message + "</li>";
        if (e.fileName != null) details += "<li>File: " + e.fileName + "</li>";
        if (e.lineNumber != null) details += "<li>Line number: " + e.lineNumber + "</li>";
        details += "</ul>";
        ORBEON.xforms.Server.showError(details, formID);
    },

    /**
     * Display the error panel and shows the specified detailed message in the detail section of the panel.
     */
    showError: function(details, formID) {
        if (!ORBEON.xforms.Globals.requestIgnoreErrors) {
            if (ORBEON.xforms.Globals.formErrorPanel[formID]) {
                ORBEON.xforms.Globals.formErrorPanel[formID].element.style.display = "block";
                ORBEON.xforms.Globals.formErrorPanel[formID].errorDetailsDiv.innerHTML = details;
                ORBEON.xforms.Globals.formErrorPanel[formID].show();
                ORBEON.xforms.Globals.formErrorPanel[formID].center();
            }
        }
    },

    fireEvents: function(events, incremental) {
        // Store the time of the first event to be sent in the queue
        var currentTime = new Date().getTime();
        if (ORBEON.xforms.Globals.eventQueue.length == 0)
            ORBEON.xforms.Globals.eventsFirstEventTime = currentTime;

        // Store events to fire
        for (var eventIndex = 0; eventIndex < events.length; eventIndex++) {
            ORBEON.xforms.Globals.eventQueue.push(events[eventIndex]);
        }

        // Fire them with a delay to give us a change to aggregate events together
        ORBEON.xforms.Globals.executeEventFunctionQueued++;
        if (incremental && !(currentTime - ORBEON.xforms.Globals.eventsFirstEventTime >
                XFORMS_DELAY_BEFORE_FORCE_INCREMENTAL_REQUEST_IN_MS)) {
            // After a delay (e.g. 500 ms), run executeNextRequest() and send queued events to server
            // if there are no other executeNextRequest() that have been added to the queue after this
            // request.
            window.setTimeout(function() { ORBEON.xforms.Server.executeNextRequest(false); },
                XFORMS_DELAY_BEFORE_INCREMENTAL_REQUEST_IN_MS);
        } else {
            // After a very short delay (e.g. 20 ms), run executeNextRequest() and force queued events
            // to be sent to the server, even if there are other executeNextRequest() queued.
            // The small delay is here so we don't send multiple requests to the server when the
            // browser gives us a sequence of events (e.g. focus out, change, focus in).
            window.setTimeout(function() { ORBEON.xforms.Server.executeNextRequest(true); },
                XFORMS_INTERNAL_SHORT_DELAY_IN_MS);
        }
        return false;
    },

    executeNextRequest: function(bypassRequestQueue) {
        bypassRequestQueue = typeof(bypassRequestQueue) == "boolean"  && bypassRequestQueue == true;

        ORBEON.xforms.Globals.executeEventFunctionQueued--;
        var executedRequest = false;
        if (!ORBEON.xforms.Globals.requestInProgress
                && ORBEON.xforms.Globals.eventQueue.length > 0
                && (bypassRequestQueue || ORBEON.xforms.Globals.executeEventFunctionQueued == 0)) {

            // Collapse value change for the same control
            {
                var seenControlValue = {};
                var newEvents = [];
                for (var eventIndex = ORBEON.xforms.Globals.eventQueue.length - 1; eventIndex >= 0; eventIndex--) {
                    // Extract information from event array
                    var event = ORBEON.xforms.Globals.eventQueue[eventIndex];
                    if (event.eventName == "xxforms-value-change-with-focus-change") {
                        // Don't send change value if there is already a change value for the same control
                        if (seenControlValue[event.targetId] == null) {
                            seenControlValue[event.targetId] = true;
                            // Don't send change value if the server already knows about the value of this control
                            if (ORBEON.util.Dom.hasClass(ORBEON.util.Dom.getElementById(event.targetId), "xforms-upload") ||
                                (ORBEON.xforms.Globals.serverValue[event.targetId] != "undefined"
                                    && ORBEON.xforms.Globals.serverValue[event.targetId] != event.value)) {
                                ORBEON.xforms.Globals.serverValue[event.targetId] = event.value;
                                newEvents.unshift(event);
                            }
                        }
                    } else {
                        newEvents.unshift(event);
                    }
                }
                ORBEON.xforms.Globals.eventQueue = newEvents;
            }

            // Check again that we have events to send after collapsing
            if (ORBEON.xforms.Globals.eventQueue.length > 0) {

                // Save the form for this request
                ORBEON.xforms.Globals.requestForm = ORBEON.xforms.Globals.eventQueue[0].form;
                var formID = ORBEON.xforms.Globals.requestForm.id;

                // Mark this as loading
                ORBEON.xforms.Globals.requestInProgress = true;
                if (XFORMS_DELAY_BEFORE_DISPLAY_LOADING_IN_MS == 0) xformsDisplayLoading();
                else window.setTimeout(xformsDisplayLoading, XFORMS_DELAY_BEFORE_DISPLAY_LOADING_IN_MS);

                // Remove from this list of ids that changed the id of controls for
                // which we have received the keyup corresponding to the keydown
                for (var id  in ORBEON.xforms.Globals.changedIdsRequest) {
                    if (ORBEON.xforms.Globals.changedIdsRequest[id] == 0)
                        ORBEON.xforms.Globals.changedIdsRequest[id] = null;
                }

                // Figure out if we will be ignoring error during this request or not
                ORBEON.xforms.Globals.requestIgnoreErrors = true;
                for (var eventIndex = 0; eventIndex < ORBEON.xforms.Globals.eventQueue.length; eventIndex++) {
                    var event = ORBEON.xforms.Globals.eventQueue[eventIndex];
                    if (!event.ignoreErrors) {
                        ORBEON.xforms.Globals.requestIgnoreErrors = false;
                        break;
                    }
                }
                
                // Build request
                var requestDocumentString = "";

                // Add entity declaration for nbsp. We are adding this as this entity is generated by the FCK editor.
                requestDocumentString += '<!DOCTYPE xxforms:event-request [<!ENTITY nbsp "&#160;">]>\n';

                var indent = "    ";
                {
                    // Start request
                    requestDocumentString += '<xxforms:event-request xmlns:xxforms="http://orbeon.org/oxf/xml/xforms">\n';

                    // Add static state
                    requestDocumentString += indent;
                    requestDocumentString += '<xxforms:static-state>';
                    requestDocumentString += ORBEON.xforms.Globals.formStaticState[formID].value;
                    requestDocumentString += '</xxforms:static-state>\n';

                    // Add dynamic state (element is just created and will be filled just before we send the request)
                    requestDocumentString += indent;
                    requestDocumentString += '<xxforms:dynamic-state>';
                    requestDocumentString += xformsGetFromClientState(formID, "ajax-dynamic-state");
                    requestDocumentString += '</xxforms:dynamic-state>\n';

                    // Start action
                    requestDocumentString += indent;
                    requestDocumentString += '<xxforms:action>\n';

                    // Add events
                    var handledEvents = [];
                    for (var i = 0; i < ORBEON.xforms.Globals.eventQueue.length; i++) {
                        var event = ORBEON.xforms.Globals.eventQueue[i];

                        // Only handle this event if it is for the form we chose
                        if (ORBEON.xforms.Controls.getForm(event.form) == ORBEON.xforms.Globals.requestForm) {
                            // Create <xxforms:event> element
                            requestDocumentString += indent + indent;
                            requestDocumentString += '<xxforms:event';
                            requestDocumentString += ' name="' + event.eventName + '"';
                            if (event.targetId != null)
                                requestDocumentString += ' source-control-id="' + event.targetId + '"';
                            if (event.otherId != null)
                                requestDocumentString += ' other-control-id="' + event.otherId + '"';
                            requestDocumentString += '>';
                            if (event.value != null) {
                                // When the range is used we get an int here when the page is first loaded
                                if (typeof event.value == "string") {
                                    event.value = event.value.replace(XFORMS_REGEXP_AMPERSAND, "&amp;");
                                    event.value = event.value.replace(XFORMS_REGEXP_OPEN_ANGLE, "&lt;");
                                }
                                requestDocumentString += event.value;
                            }
                            requestDocumentString += '</xxforms:event>\n';
                            handledEvents.unshift(i);
                        }
                    }

                    // End action
                    requestDocumentString += indent;
                    requestDocumentString += '</xxforms:action>\n';

                    // End request
                    requestDocumentString += '</xxforms:event-request>';

                    // Remove events we have handled from event queue
                    for (var i = 0; i < handledEvents.length; i++)
                        ORBEON.xforms.Globals.eventQueue.splice(handledEvents[i], 1);
                }

                // Send request
                executedRequest = true;
                try {
                    YAHOO.util.Connect.setDefaultPostHeader(false);
                    YAHOO.util.Connect.initHeader("Content-Type", "application/xml");
                    var callback = {
                        success: ORBEON.xforms.Server.handleResponse,
                        failure: ORBEON.xforms.Server.handleFailure
                    }
                    YAHOO.util.Connect.asyncRequest("POST", XFORMS_SERVER_URL, callback, requestDocumentString);
                } catch (e) {
                    ORBEON.xforms.Globals.requestInProgress = false;
                    ORBEON.xforms.Server.exceptionWhenTalkingToServer(e, formID);
                }
            }
        }

        // Hide loading indicator if we have not started a new request (nothing more to run)
        // and there are not events in the queue. However make sure not to hide the error message
        // if the last XHR query returned an error.
        if (!executedRequest && ORBEON.xforms.Globals.eventQueue.length == 0)
            xformsDisplayIndicator("none");
    },

    handleFailure: function(o) {
        ORBEON.xforms.Globals.requestInProgress = false;
        var formID = ORBEON.xforms.Globals.requestForm.id;
        var details = "Error while processing response: " + (o.responseText !== undefined ? o.responseText : o.statusText);
        if (ORBEON.xforms.Globals.isRenderingEngineGecko && o.statusText == "communication failure") {
            // On Firefox, when the user navigates to another page while an Ajax request is in progress,
            // we receive an error here, which we don't want to display. We don't have a good way of knowning if we get
            // this error because there was really a communication failure or if this is because the user is
            // going to another page. So we wait some time before showing the error, hoping that if another page is
            // loading, that other page will be loaded by the time our timeout expires.
            window.setTimeout(function() { ORBEON.xforms.Server.showError(details, formID); },
                XFORMS_DELAY_BEFORE_GECKO_COMMUNICATION_ERROR_IN_MS);
        } else {
            // Display alert right away
            ORBEON.xforms.Server.showError(details, formID);
        }
    },

    handleUploadResponse: function(o) {
        // Clear all the upload fields (currently we submit all of them, but in the future, should clear only the ones that have been submitted)
        var uploadElements = YAHOO.util.Dom.getElementsByClassName("xforms-upload", "span");
        for (var uploadIndex = 0; uploadIndex < uploadElements.length; uploadIndex++) {
            var uploadElement = uploadElements[uploadIndex];
            if (ORBEON.util.Dom.hasClass(uploadElement, "xforms-upload-state-empty"))// this also excludes templates
                ORBEON.util.Dom.clearUploadControl(uploadElement);
        }
        ORBEON.xforms.Server.handleResponse(o);
    },

    handleResponse: function(o) {

        var formID = ORBEON.xforms.Globals.requestForm.id;
        var responseXML = o.responseXML;
        if (!responseXML || (responseXML && responseXML.documentElement && responseXML.documentElement.tagName.toLowerCase() == "html")) {
            // The XML docucment does not come in o.responseXML: parse o.responseText.
            // This happens in particular when we get a response after a background upload.
            var xmlString = o.responseText.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
            responseXML = ORBEON.util.Dom.stringToDom(xmlString);
        }

        try {
            if (responseXML && responseXML.documentElement
                    && responseXML.documentElement.tagName.indexOf("event-response") != -1) {

                // Good: we received an XML document from the server
                var responseRoot = responseXML.documentElement;
                var newDynamicState = null;

                // Whether this response has triggered a load which will replace the current page.
                var newDynamicStateTriggersReplace = false;

                for (var i = 0; i < responseRoot.childNodes.length; i++) {

                    // Update instances
                    if (xformsGetLocalName(responseRoot.childNodes[i]) == "dynamic-state") {
                        newDynamicState = ORBEON.util.Dom.getStringValue(responseRoot.childNodes[i]);
                        // Store new dynamic state as soon as we find it. This is because the server only keeps the last
                        // dynamic state. So if a JavaScript error happens later on while processing the response,
                        // the next request we do we still want to send the latest dynamic state known to the server.
                        xformsStoreInClientState(formID, "ajax-dynamic-state", newDynamicState);
                    }

                    if (xformsGetLocalName(responseRoot.childNodes[i]) == "action") {
                        var actionElement = responseRoot.childNodes[i];

                        // Firt repeat and delete "lines" in repeat (as itemset changed below might be in a new line)
                        for (var actionIndex = 0; actionIndex < actionElement.childNodes.length; actionIndex++) {
                            var actionName = xformsGetLocalName(actionElement.childNodes[actionIndex]);
                            switch (actionName) {

                                case "control-values": {
                                    var controlValuesElement = actionElement.childNodes[actionIndex];
                                    for (var j = 0; j < controlValuesElement.childNodes.length; j++) {
                                        var controlValueAction = xformsGetLocalName(controlValuesElement.childNodes[j]);
                                        switch (controlValueAction) {

                                            // Copy repeat template
                                            case "copy-repeat-template": {
                                                var copyRepeatTemplateElement = controlValuesElement.childNodes[j];
                                                var repeatId = ORBEON.util.Dom.getAttribute(copyRepeatTemplateElement, "id");
                                                var parentIndexes = ORBEON.util.Dom.getAttribute(copyRepeatTemplateElement, "parent-indexes");
                                                var idSuffix = ORBEON.util.Dom.getAttribute(copyRepeatTemplateElement, "id-suffix");
                                                // Put nodes of the template in an array
                                                var templateNodes = new Array();
                                                {
                                                    // Locate end of the repeat
                                                    var delimiterTagName = null;
                                                    var templateRepeatEnd = ORBEON.util.Dom.getElementById("repeat-end-" + repeatId);
                                                    var templateNode = templateRepeatEnd.previousSibling;
                                                    var nestedRepeatLevel = 0;
                                                    while (!(nestedRepeatLevel == 0 && templateNode.nodeType == ELEMENT_TYPE
                                                             && ORBEON.util.Dom.hasClass(templateNode, "xforms-repeat-delimiter"))) {
                                                        var nodeCopy = templateNode.cloneNode(true);
                                                        if (templateNode.nodeType == ELEMENT_TYPE) {
                                                            // Save tag name to be used for delimiter
                                                            delimiterTagName = templateNode.tagName;
                                                            // Decrement nestedRepeatLevel when we we exit a nested repeat
                                                            if (ORBEON.util.Dom.hasClass(templateNode, "xforms-repeat-begin-end") &&
                                                                    templateNode.id.indexOf("repeat-begin-") == 0)
                                                                nestedRepeatLevel--;
                                                            // Add suffix to all the ids
                                                            xformsAddSuffixToIds(nodeCopy, parentIndexes == "" ? idSuffix : parentIndexes + XFORMS_SEPARATOR_2 + idSuffix, nestedRepeatLevel);
                                                            // Increment nestedRepeatLevel when we enter a nested repeat
                                                            if (ORBEON.util.Dom.hasClass(templateNode, "xforms-repeat-begin-end") &&
                                                                    templateNode.id.indexOf("repeat-end-") == 0)
                                                                nestedRepeatLevel++;
                                                            // Remove "xforms-repeat-template" from classes on copy of element
                                                            var nodeCopyClasses = nodeCopy.className.split(" ");
                                                            var nodeCopyNewClasses = new Array();
                                                            for (var nodeCopyClassIndex = 0; nodeCopyClassIndex < nodeCopyClasses.length; nodeCopyClassIndex++) {
                                                                var currentClass = nodeCopyClasses[nodeCopyClassIndex];
                                                                if (currentClass != "xforms-repeat-template")
                                                                    nodeCopyNewClasses.push(currentClass);
                                                            }
                                                            nodeCopy.className = nodeCopyNewClasses.join(" ");
                                                        }
                                                        templateNodes.push(nodeCopy);
                                                        templateNode = templateNode.previousSibling;
                                                    }
                                                    // Add a delimiter
                                                    var newDelimiter = document.createElement(delimiterTagName);
                                                    newDelimiter.className = "xforms-repeat-delimiter";
                                                    templateNodes.push(newDelimiter);
                                                    // Reverse nodes as they were inserted in reverse order
                                                    templateNodes = templateNodes.reverse();
                                                }
                                                // Find element after insertion point
                                                var afterInsertionPoint;
                                                {
                                                    if (parentIndexes == "") {
                                                        // Top level repeat: contains a template
                                                        var repeatEnd = ORBEON.util.Dom.getElementById("repeat-end-" + repeatId);
                                                        var cursor = repeatEnd.previousSibling;
                                                        while (!(cursor.nodeType == ELEMENT_TYPE
                                                                && ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-delimiter")
                                                                && !ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-template"))) {
                                                            cursor = cursor.previousSibling;
                                                        }
                                                        afterInsertionPoint = cursor;
                                                        // Nested repeat: does not contain a template
                                                    } else {
                                                        var repeatEnd = ORBEON.util.Dom.getElementById("repeat-end-" + xformsAppendRepeatSuffix(repeatId, parentIndexes));
                                                        afterInsertionPoint = repeatEnd;
                                                    }
                                                }
                                                // Insert copy of template nodes
                                                for (var templateNodeIndex = 0; templateNodeIndex < templateNodes.length; templateNodeIndex++) {
                                                    templateNode = templateNodes[templateNodeIndex];
                                                    afterInsertionPoint.parentNode.insertBefore(templateNode, afterInsertionPoint);
                                                    ORBEON.xforms.Init.insertedElement(templateNode);
                                                }
                                                // Initialize newly added form elements
                                                if (! ORBEON.xforms.Globals.supportsCaptureEvents)
                                                    ORBEON.xforms.Init.registerListenersOnFormElements();
                                                break;
                                            }


                                            // Delete element in repeat
                                            case "delete-repeat-elements": {
                                                // Extract data from server response
                                                var deleteElementElement = controlValuesElement.childNodes[j];
                                                var deleteId = ORBEON.util.Dom.getAttribute(deleteElementElement, "id");
                                                var parentIndexes = ORBEON.util.Dom.getAttribute(deleteElementElement, "parent-indexes");
                                                var count = ORBEON.util.Dom.getAttribute(deleteElementElement, "count");
                                                // Find end of the repeat
                                                var repeatEnd = ORBEON.util.Dom.getElementById("repeat-end-" + xformsAppendRepeatSuffix(deleteId, parentIndexes));
                                                // Find last element to delete
                                                var lastElementToDelete;
                                                {
                                                    lastElementToDelete = repeatEnd.previousSibling;
                                                    if (parentIndexes == "") {
                                                        // Top-level repeat: need to go over template
                                                        while (true) {
                                                            // Look for delimiter that comes just before the template
                                                            if (lastElementToDelete.nodeType == ELEMENT_TYPE
                                                                    && ORBEON.util.Dom.hasClass(lastElementToDelete, "xforms-repeat-delimiter")
                                                                    && !ORBEON.util.Dom.hasClass(lastElementToDelete, "xforms-repeat-template"))
                                                                break;
                                                            lastElementToDelete = lastElementToDelete.previousSibling;
                                                        }
                                                        lastElementToDelete = lastElementToDelete.previousSibling;
                                                    }
                                                }
                                                // Perform delete
                                                for (var countIndex = 0; countIndex < count; countIndex++) {
                                                    var nestedRepeatLevel = 0;
                                                    while (true) {
                                                        var wasDelimiter = false;
                                                        if (lastElementToDelete.nodeType == ELEMENT_TYPE) {
                                                            if (ORBEON.util.Dom.hasClass(lastElementToDelete, "xforms-repeat-begin-end") &&
                                                                    lastElementToDelete.id.indexOf("repeat-end-") == 0) {
                                                                // Entering nested repeat
                                                                nestedRepeatLevel++;
                                                            } else if (ORBEON.util.Dom.hasClass(lastElementToDelete, "xforms-repeat-begin-end") &&
                                                                    lastElementToDelete.id.indexOf("repeat-begin-") == 0) {
                                                                // Exiting nested repeat
                                                                nestedRepeatLevel--;
                                                            } else {
                                                                wasDelimiter = nestedRepeatLevel == 0 &&
                                                                   ORBEON.util.Dom.hasClass(lastElementToDelete, "xforms-repeat-delimiter");
                                                            }
                                                        }
                                                        var previous = lastElementToDelete.previousSibling;
                                                        lastElementToDelete.parentNode.removeChild(lastElementToDelete);
                                                        lastElementToDelete = previous;
                                                        if (wasDelimiter) break;
                                                    }
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // Second handle the <xxforms:itemsets> actions (we want to do this before we set the value of
                        // controls as the value of the select might be in the new values of the itemset).
                        for (var actionIndex = 0; actionIndex < actionElement.childNodes.length; actionIndex++) {
                            // Change values in an itemset
                            if (xformsGetLocalName(actionElement.childNodes[actionIndex]) == "itemsets") {
                                var itemsetsElement = actionElement.childNodes[actionIndex];
                                for (var j = 0; j < itemsetsElement.childNodes.length; j++) {
                                    if (xformsGetLocalName(itemsetsElement.childNodes[j]) == "itemset") {
                                        var itemsetElement = itemsetsElement.childNodes[j];
                                        var itemsetTree = ORBEON.util.String.eval(ORBEON.util.Dom.getStringValue(itemsetElement));
                                        var controlId = ORBEON.util.Dom.getAttribute(itemsetElement, "id");
                                        var documentElement = ORBEON.util.Dom.getElementById(controlId);
                                        var documentElementClasses = documentElement.className.split(" ");

                                        if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-open")) {

                                            // Build list with new values
                                            var newValues = new Array();
                                            for (var topIndex = 0; topIndex < itemsetTree.length; topIndex++)
                                                newValues.push(itemsetTree[topIndex][1]);

                                            // Case of the auto-complete control
                                            var textfield = ORBEON.util.Dom.getChildElementByIndex(documentElement, 0);
                                            textfield.actb_keywords = newValues;
                                            // Reopen auto-complete if necessary
                                            var lastKeyCode = ORBEON.xforms.Globals.autoCompleteLastKeyCode[documentElement.id];
                                            if (lastKeyCode != null)
                                                ORBEON.xforms.Globals.autoCompleteOpen[documentElement.id](lastKeyCode);

                                        } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-xxforms-tree")
                                                || ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-xxforms-tree")) {

                                            // Case of a tree

                                            // Remove markup for current tree
                                            var yuiTree = ORBEON.xforms.Globals.treeYui[documentElement.id];
                                            var yuiRoot = yuiTree.getRoot();
                                            yuiTree.removeChildren(yuiRoot);
                                            // Expand root. If we don't the tree with checkboxes does not show.
                                            yuiRoot.expand();

                                            // Re-populate the tree
                                            ORBEON.xforms.Init._initTreeDivFromArray(documentElement, yuiTree, itemsetTree);

                                        } else if (documentElement.tagName == "SELECT") {

                                            // Case of list / combobox
                                            var options = documentElement.options;

                                            // Remember selected values
                                            var selectedValueCount = 0;
                                            var selectedValues = new Array();
                                            for (var k = 0; k < options.length; k++) {
                                                if (options[k].selected) {
                                                    selectedValues[selectedValueCount] = options[k].value;
                                                    selectedValueCount++;
                                                }
                                            }

                                            // Utility function to generate an option
                                            function generateOption(label, value, selectedValues) {
                                                var selected = xformsArrayContains(selectedValues, value);
                                                return '<option value="' + ORBEON.util.String.escapeAttribute(value) + '"'
                                                        + (selected ? ' selected="selected"' : '') + '>' + label + '</option>';
                                            }

                                            // Build new content for the select element
                                            var sb = new Array();
                                            for (var topIndex = 0; topIndex < itemsetTree.length; topIndex++) {
                                                var itemElement = itemsetTree[topIndex];
                                                var label = itemElement[0];
                                                var value = itemElement[1];
                                                if (itemElement.length > 2) {
                                                    // This is item that contains other elements
                                                    sb[sb.length] = '<optgroup label="' + ORBEON.util.String.escapeAttribute(label) + '">';
                                                    // Go through options in this optgroup
                                                    for (var innerIndex = 2; innerIndex < itemElement.length; innerIndex++) {
                                                        var itemElementOption = itemElement[innerIndex];
                                                        sb[sb.length] = generateOption(itemElementOption[0], itemElementOption[1], selectedValues);
                                                    }
                                                    sb[sb.length] = '</optgroup>';
                                                } else {
                                                    // This item is directly an option
                                                    sb[sb.length] = generateOption(label, value, selectedValues);
                                                }
                                            }

                                            // Set content of select element
                                            if (ORBEON.xforms.Globals.isRenderingEngineTridend) {
                                                // IE does not support setting the content of a select with innerHTML
                                                // So we have to generate the whole select, and use outerHTML
                                                documentElement.innerHTML = "";
                                                documentElement.outerHTML =
                                                    documentElement.outerHTML.substring(0, documentElement.outerHTML.indexOf("</SELECT>"))
                                                    + sb.join("") + "</select>";
                                                // Get again control, as it has been re-created
                                                documentElement = ORBEON.util.Dom.getElementById(controlId);
                                                // Re-register handlers on the newly created control
                                                ORBEON.xforms.Init.registerListenersOnFormElement(documentElement);
                                            } else {
                                                // Version for compliant browsers
                                                documentElement.innerHTML = sb.join("");
                                            }

                                        } else {

                                            // Case of checkboxes / radio buttons

                                            // Actual values:
                                            //     <span>
                                            //         <input type="checkbox" checked="" value="v" name="xforms-element-97" id="element-97-opsitem0"/>
                                            //         <label for="xforms-element-97-opsitem0" id="xforms-element-99">Vanilla</label>
                                            //     </span>
                                            //
                                            // Template follows:
                                            //     <span>
                                            //         <input type="checkbox" value="$xforms-template-value$" name="xforms-element-97" id="xforms-element-97-opsitem0"/>
                                            //         <label for="xforms-element-97-opsitem0" id="xforms-element-99">$xforms-template-label$</label>
                                            //     </span>

                                            // Get element following control
                                            var template = documentElement.nextSibling;
                                            while (template.nodeType != ELEMENT_TYPE)
                                                template = template.nextSibling;

                                            // Get its child element (a span that contains an input)
                                            template = template.firstChild;
                                            while (template.nodeType != ELEMENT_TYPE)
                                                template = template.nextSibling;

                                            // Remove content and store current checked value
                                            var valueToChecked = new Array();
                                            while (documentElement.childNodes.length > 0) {
                                                var input = xformsGetInputUnderNode(documentElement.firstChild);
                                                valueToChecked[input.value] = input.checked;
                                                documentElement.removeChild(documentElement.firstChild);
                                            }

                                            // Recreate content based on template
                                            var itemIndex = 0;
                                            for (var k = 0; k < itemsetTree.length; k++) {
                                                var itemElement = itemsetTree[k];
                                                var templateClone = template.cloneNode(true);
                                                xformsStringReplace(templateClone, "$xforms-template-label$", itemElement[0]);
                                                xformsStringReplace(templateClone, "$xforms-template-value$", itemElement[1]);
                                                xformsStringReplace(templateClone, "$xforms-item-index$", itemIndex);
                                                documentElement.appendChild(templateClone);
                                                // Restore checked state after copy
                                                if (valueToChecked[itemElement[1]] == true)
                                                    xformsGetInputUnderNode(templateClone).checked = true;
                                                itemIndex++;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // Handle other actions
                        var serverEventsIndex = -1;
                        for (var actionIndex = 0; actionIndex < actionElement.childNodes.length; actionIndex++) {

                            var actionName = xformsGetLocalName(actionElement.childNodes[actionIndex]);
                            switch (actionName) {

                                // Update controls
                                case "control-values": {
                                    var controlValuesElement = actionElement.childNodes[actionIndex];
                                    for (var j = 0; j < controlValuesElement.childNodes.length; j++) {
                                        var controlValueAction = xformsGetLocalName(controlValuesElement.childNodes[j]);
                                        switch (controlValueAction) {

                                            // Update control value and MIPs
                                            case "control": {
                                                var controlElement = controlValuesElement.childNodes[j];
                                                var newControlValue = ORBEON.util.Dom.getStringValue(controlElement);
                                                var controlId = ORBEON.util.Dom.getAttribute(controlElement, "id");
                                                var staticReadonly = ORBEON.util.Dom.getAttribute(controlElement, "static");
                                                var relevant = ORBEON.util.Dom.getAttribute(controlElement, "relevant");
                                                var readonly = ORBEON.util.Dom.getAttribute(controlElement, "readonly");
                                                var required = ORBEON.util.Dom.getAttribute(controlElement, "required");
                                                var displayValue = ORBEON.util.Dom.getAttribute(controlElement, "display-value");
                                                var type = ORBEON.util.Dom.getAttribute(controlElement, "type");
                                                var documentElement = ORBEON.util.Dom.getElementById(controlId);
                                                if (documentElement == null) {
                                                    documentElement = ORBEON.util.Dom.getElementById("group-begin-" + controlId);
                                                }
                                                var documentElementClasses = documentElement.className.split(" ");
                                                var isControl = ORBEON.util.Dom.hasClass(documentElement, "xforms-control");

                                                // Save new value sent by server (upload controls don't carry their value the same way as other controls)
                                                var previousServerValue = ORBEON.xforms.Globals.serverValue[controlId];
                                                if (!ORBEON.util.Dom.hasClass(documentElement, "xforms-upload"))
                                                    ORBEON.xforms.Globals.serverValue[controlId] = newControlValue;

                                                // Handle migration of control from non-static to static if needed
                                                var isStaticReadonly = ORBEON.util.Dom.hasClass(documentElement, "xforms-static");
                                                if (!isStaticReadonly && staticReadonly == "true") {
                                                    if (isControl) {
                                                        // Replace existing element with span
                                                        var parentElement = documentElement.parentNode;
                                                        var newDocumentElement = document.createElement("span");
                                                        newDocumentElement.setAttribute("id", controlId);
                                                        newDocumentElement.setAttribute("class", documentElementClasses.join(" ") + " xforms-static");
                                                        parentElement.replaceChild(newDocumentElement, documentElement);
                                                        // Remove alert
                                                        var alertElement = ORBEON.xforms.Controls._getControlLabel(newDocumentElement, "xforms-alert");
                                                        if (alertElement != null)
                                                            parentElement.removeChild(alertElement);
                                                        // Remove hint
                                                        var hintLabel = ORBEON.xforms.Events._findHint(newDocumentElement);
                                                        if (hintLabel != null)
                                                            parentElement.removeChild(hintLabel);
                                                        // Update document element information
                                                        documentElement = newDocumentElement;
                                                    } else {
                                                        // Just add the new class
                                                        ORBEON.util.Dom.addClass(documentElement, "xforms-static");
                                                    }
                                                    isStaticReadonly = true;
                                                    documentElementClasses = documentElement.className.split(" ");
                                                }

                                                // We update the relevance and readonly before we update the value. If we don't, updating the value
                                                // can fail on IE in some cases. (The details about this issue have been lost.)

                                                // Handle relevance
                                                if (relevant != null) {
                                                    var isRelevant = relevant == "true";
                                                    ORBEON.xforms.Controls.setRelevant(documentElement, isRelevant);
                                                    // Autosize textarea
                                                    if (ORBEON.util.Dom.hasClass(documentElement, "xforms-textarea-appearance-xxforms-autosize")) {
                                                        ORBEON.xforms.Controls.autosizeTextarea(documentElement);
                                                    }
                                                }

                                                // Handle required
                                                if (required != null) {
                                                    var isRequired = required == "true";
                                                    if (isRequired) ORBEON.util.Dom.addClass(documentElement, "xforms-required");
                                                    else ORBEON.util.Dom.removeClass(documentElement, "xforms-required");
                                                }

                                                // Handle readonly
                                                if (readonly != null && !isStaticReadonly) {
                                                    function setReadonlyOnFormElement(element, isReadonly) {
                                                        if (isReadonly) {
                                                            element.setAttribute("disabled", "disabled");
                                                            ORBEON.util.Dom.addClass(element, "xforms-readonly");
                                                        } else {
                                                            element.removeAttribute("disabled");
                                                            ORBEON.util.Dom.removeClass(element, "xforms-readonly");
                                                        }
                                                    }

                                                    var isReadonly = readonly == "true";
                                                    if (ORBEON.util.Dom.hasClass(documentElement, "xforms-input") && !ORBEON.util.Dom.hasClass(documentElement, "xforms-type-boolean")) {
                                                        // XForms input

                                                        // Display value
                                                        var displaySpan = documentElement.firstChild;
                                                        while (displaySpan.nodeType != ELEMENT_TYPE) displaySpan = displaySpan.nextSibling;
                                                        if (isReadonly) ORBEON.util.Dom.addClass(displaySpan, "xforms-readonly");
                                                        else ORBEON.util.Dom.removeClass(displaySpan, "xforms-readonly");

                                                        // Text field
                                                        var textField = displaySpan.nextSibling;
                                                        while (textField.nodeType != ELEMENT_TYPE) textField = textField.nextSibling;
                                                        if (isReadonly) textField.setAttribute("disabled", "disabled");
                                                        else textField.removeAttribute("disabled");

                                                        // Calendar picker
                                                        var showCalendar = textField.nextSibling;
                                                        while (showCalendar.nodeType != ELEMENT_TYPE) showCalendar = showCalendar.nextSibling;
                                                        if (isReadonly) ORBEON.util.Dom.addClass(showCalendar, "xforms-showcalendar-readonly");
                                                        else ORBEON.util.Dom.removeClass(showCalendar, "xforms-showcalendar-readonly");
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-output")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-group")) {
                                                        // XForms output and group
                                                        if (isReadonly) ORBEON.util.Dom.addClass(documentElement, "xforms-readonly");
                                                        else ORBEON.util.Dom.removeClass(documentElement, "xforms-readonly");
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-full")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-full")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-input-appearance-full")) {
                                                        // XForms radio buttons
                                                        for (var spanIndex = 0; spanIndex < documentElement.childNodes.length; spanIndex++) {
                                                            var span = documentElement.childNodes[spanIndex];
                                                            var input = span.firstChild;
                                                            setReadonlyOnFormElement(input, isReadonly);
                                                        }
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-xxforms-autocomplete")) {
                                                        // Auto-complete field
                                                        var input = ORBEON.util.Dom.getChildElementByIndex(documentElement, 0);
                                                        setReadonlyOnFormElement(input, isReadonly);
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-textarea") && ORBEON.util.Dom.hasClass(documentElement, "xforms-mediatype-text-html")) {
                                                        // XForms HTML area
                                                        var htmlEditor = FCKeditorAPI.GetInstance(documentElement.name);
                                                        if (isReadonly) {
                                                            htmlEditor.ToolbarSet.Collapse();
                                                            // TO-DO
                                                        } else {
                                                            htmlEditor.ToolbarSet.Expand();
                                                            // TO-DO
                                                        }
                                                    } else {
                                                        // Other controls
                                                        setReadonlyOnFormElement(documentElement, isReadonly);
                                                    }
                                                }

                                                // Update value
                                                if (isControl) {
                                                    if (ORBEON.util.Dom.hasClass(documentElement, "xforms-output") || isStaticReadonly) {
                                                        // XForms output or "static readonly" mode
                                                        var newOutputControlValue = displayValue != null ? displayValue : newControlValue;
                                                        if (ORBEON.util.Dom.hasClass(documentElement, "xforms-mediatype-image")) {
                                                            var image = ORBEON.util.Dom.getChildElementByIndex(documentElement, 0);
                                                            image.src = newOutputControlValue;
                                                        } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-mediatype-text-html")) {
                                                            documentElement.innerHTML = newOutputControlValue;
                                                        } else {
                                                            ORBEON.util.Dom.setStringValue(documentElement, newOutputControlValue);
                                                        }
                                                    } else if (ORBEON.xforms.Globals.changedIdsRequest[controlId] != null) {
                                                        // User has modified the value of this control since we sent our request:
                                                        // so don't try to update it
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-trigger")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-submit")) {
                                                        // Triggers don't have a value: don't update them
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-open")) {
                                                        // Auto-complete
                                                        if (documentElement.value != newControlValue) {
                                                            documentElement.value = newControlValue;
                                                            ORBEON.util.Dom.getChildElementByIndex(documentElement, 0).value = newControlValue;
                                                            documentElement.previousValue = newControlValue;
                                                        }
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-full")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-full")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-input-appearance-full")) {
                                                        // Handle checkboxes and radio buttons
                                                        var selectedValues = ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-full")
                                                            ? newControlValue.split(" ") : new Array(newControlValue);
                                                        var checkboxInputs = documentElement.getElementsByTagName("input");
                                                        for (var checkboxInputIndex = 0; checkboxInputIndex < checkboxInputs.length; checkboxInputIndex++) {
                                                            var checkboxInput = checkboxInputs[checkboxInputIndex];
                                                            checkboxInput.checked = xformsArrayContains(selectedValues, checkboxInput.value);
                                                        }
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-compact")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-compact")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-minimal")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-input-appearance-compact")
                                                            || ORBEON.util.Dom.hasClass(documentElement, "xforms-input-appearance-minimal")) {
                                                        // Handle lists and comboboxes
                                                        var selectedValues = ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-compact")
                                                            ? newControlValue.split(" ") : new Array(newControlValue);
                                                        var options = documentElement.options;
                                                        for (var optionIndex = 0; optionIndex < options.length; optionIndex++) {
                                                            var option = options[optionIndex];
                                                            try {
                                                                option.selected = xformsArrayContains(selectedValues, option.value);
                                                            } catch (e) {
                                                                // nop
                                                                //
                                                                // This is to prevent the error "Could not set the selected property. Unspecified error." in IE.
                                                                // Like noted in this blog entry: http://ianso.blogspot.com/2005_10_01_ianso_archive.html (search
                                                                // for the error message), it seems that DOM updates are somewhat asynchrous and that when you
                                                                // make an element visible and change a property right after that, it is sometimes as if the element
                                                                // is not visible yet, and so the property cannot be changed.
                                                            }
                                                        }
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-input") && !ORBEON.util.Dom.hasClass(documentElement, "xforms-type-boolean")) {
                                                        // XForms input
                                                        var displayField = ORBEON.util.Dom.getChildElementByIndex(documentElement, 0);
                                                        var inputField = ORBEON.util.Dom.getChildElementByIndex(documentElement, 1);
                                                        var datePicker = ORBEON.util.Dom.getChildElementByIndex(documentElement, 2);

                                                        // Change classes on control and date pick based on type
                                                        if (type == "{http://www.w3.org/2001/XMLSchema}date") {
//                                                            for (var childIndex = 0; childIndex < documentElement.childNodes.length; childIndex++) {
//                                                                var child = documentElement.childNodes[childIndex];
                                                                ORBEON.util.Dom.addClass(documentElement, "xforms-type-date");
                                                                ORBEON.util.Dom.removeClass(documentElement, "xforms-type-string");
//                                                            }
                                                        } else if (type != null && type != "{http://www.w3.org/2001/XMLSchema}date") {
//                                                            for (var childIndex = 0; childIndex < documentElement.childNodes.length; childIndex++) {
//                                                                var child = documentElement.childNodes[childIndex];
//                                                                ORBEON.util.Dom.addClass(documentElement, "xforms-type-string");
                                                                ORBEON.util.Dom.removeClass(documentElement, "xforms-type-date");
//                                                            }
                                                        }

                                                        // Populate values
                                                        if (ORBEON.util.Dom.hasClass(documentElement, "xforms-type-date")) {
                                                            ORBEON.util.Dom.setStringValue(displayField, (displayValue == null || displayValue == "") ? "\u00a0" : displayValue);
                                                        }
                                                        if (documentElement.value != newControlValue) {
                                                            documentElement.previousValue = newControlValue;
                                                            documentElement.valueSetByXForms++;
                                                            documentElement.value = newControlValue;
                                                        }
                                                        if (inputField.value != newControlValue)
                                                            inputField.value = newControlValue;
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-textarea")
                                                            && ORBEON.util.Dom.hasClass(documentElement, "xforms-mediatype-text-html")) {
                                                        // HTML area
                                                        var htmlEditor = FCKeditorAPI.GetInstance(documentElement.name);
                                                        var doUpdate =
                                                                // Update only if the new value is different than the value already have in the HTML area
                                                                xformsNormalizeEndlines(htmlEditor.GetXHTML()) != xformsNormalizeEndlines(newControlValue)
                                                                // Also update only if the value in the HTML area is the same now as it was when we sent it to the server
                                                                // If there is no previousServerValue, go ahead and update field
                                                                && (previousServerValue == null || htmlEditor.GetXHTML() == previousServerValue);
                                                        if (doUpdate) {
                                                            // Directly modify the DOM instead of using SetHTML() provided by the FCKeditor,
                                                            // as we loose our listeners after using the later
                                                            htmlEditor.EditorDocument.body.innerHTML = newControlValue;
                                                            // Set again the server value based on the HTML as seen from the field. HTML changes slightly when it
                                                            // is pasted in the FCK editor. The server value will be compared to the field value, to (a) figure out
                                                            // if we need to send the value again to the server and (b) to figure out if the FCK editor has been edited
                                                            // since the last time we sent the value to the serer. The bottom line is that we are going to compare
                                                            // the server value to the content of the field. So storing the value as seen by the field vs. as seen by
                                                            // server accounts for the slight difference there might be in those 2 representations.
                                                            ORBEON.xforms.Globals.serverValue[controlId] = htmlEditor.GetXHTML();
                                                            documentElement.value = newControlValue;
                                                            documentElement.previousValue = newControlValue;
                                                        }
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select-appearance-xxforms-tree")) {
                                                        // Select tree
                                                        var values = newControlValue.split(" ");
                                                        var yuiTree = ORBEON.xforms.Globals.treeYui[documentElement.id];
                                                        for (nodeIndex in yuiTree._nodes) {
                                                            var node = yuiTree._nodes[nodeIndex];
                                                            if (node.children.length == 0) {
                                                                var checked = xformsArrayContains(values, node.data.value);
                                                                if (checked) node.check(); else node.uncheck();
                                                            }
                                                        }
                                                        documentElement.value = newControlValue;
                                                        documentElement.previousValue = newControlValue;

                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-select1-appearance-xxforms-tree")) {
                                                        // Select1 tree
                                                        // Make sure the tree is open enough so the node with the new value is visible
                                                        var yuiTree = ORBEON.xforms.Globals.treeYui[documentElement.id];
                                                        ORBEON.xforms.Controls.treeOpenSelectedVisible(yuiTree, [newControlValue]);
                                                        // Deselect old value, select new value
                                                        var oldNode = yuiTree.getNodeByProperty("value", documentElement.value);
                                                        var newNode = yuiTree.getNodeByProperty("value", newControlValue);
                                                        if (oldNode != null)
                                                            YAHOO.util.Dom.removeClass(oldNode.getLabelEl(), "xforms-tree-label-selected");
                                                        if (newNode != null)
                                                            YAHOO.util.Dom.addClass(newNode.getLabelEl(), "xforms-tree-label-selected");
                                                        // Update value
                                                        documentElement.value = newControlValue;
                                                        documentElement.previousValue = newControlValue;
                                                    } else if (ORBEON.util.Dom.hasClass(documentElement, "xforms-upload")) {
                                                        // Upload

                                                        // <xxforms:control id="xforms-control-id"
                                                        //    state="empty|file"
                                                        //    filename="filename.txt" mediatype="text/plain" size="23kb"/>

                                                        // Get attributes from response
                                                        var state = ORBEON.util.Dom.getAttribute(controlElement, "state");
                                                        var filename = ORBEON.util.Dom.getAttribute(controlElement, "filename");
                                                        var mediatype = ORBEON.util.Dom.getAttribute(controlElement, "mediatype");
                                                        var size = ORBEON.util.Dom.getAttribute(controlElement, "size");
                                                        // Get elements we want to modify from the DOM
                                                        var fileInfoSpan = ORBEON.util.Dom.getChildElementByClass(documentElement, "xforms-upload-info");
                                                        var fileNameSpan = ORBEON.util.Dom.getChildElementByClass(fileInfoSpan, "xforms-upload-filename");
                                                        var mediatypeSpan = ORBEON.util.Dom.getChildElementByClass(fileInfoSpan, "xforms-upload-mediatype");
                                                        var sizeSpan = ORBEON.util.Dom.getChildElementByClass(fileInfoSpan, "xforms-upload-size");
                                                        // Set values in DOM
                                                        if (state == "empty") {
                                                            ORBEON.util.Dom.removeClass(documentElement, "xforms-upload-state-file")
                                                            ORBEON.util.Dom.addClass(documentElement, "xforms-upload-state-empty")
                                                        }
                                                        if (state == "file") {
                                                            ORBEON.util.Dom.removeClass(documentElement, "xforms-upload-state-empty")
                                                            ORBEON.util.Dom.addClass(documentElement, "xforms-upload-state-file")

                                                            // Clear upload input by replacing the control
                                                            ORBEON.util.Dom.clearUploadControl(documentElement);
                                                        }
                                                        if (filename != null)
                                                            ORBEON.util.Dom.setStringValue(fileNameSpan, filename);
                                                        if (mediatype != null)
                                                            ORBEON.util.Dom.setStringValue(mediatypeSpan, mediatype);
                                                        if (size != null)
                                                            ORBEON.util.Dom.setStringValue(sizeSpan, size);
                                                    } else if (typeof(documentElement.value) == "string") {
                                                        // Textarea, password
                                                        if (xformsNormalizeEndlines(documentElement.value) != xformsNormalizeEndlines(newControlValue)) {
                                                            documentElement.value = newControlValue;
                                                            documentElement.previousValue = newControlValue;
                                                            // Autosize textarea
                                                            if (ORBEON.util.Dom.hasClass(documentElement, "xforms-textarea-appearance-xxforms-autosize")) {
                                                                ORBEON.xforms.Controls.autosizeTextarea(documentElement);
                                                            }
                                                        }
                                                    }
                                                }

                                                // Update the required-empty/required-full even if the required has not changed or
                                                // is not specified as the value may have changed
                                                if (!isStaticReadonly) {
                                                    ORBEON.xforms.Controls.updateRequiredEmpty(documentElement);
                                                }

                                                // Store new label message in control attribute
                                                var newLabel = ORBEON.util.Dom.getAttribute(controlElement, "label");
                                                if (newLabel != null)
                                                    ORBEON.xforms.Controls.setLabelMessage(documentElement, newLabel);
                                                // Store new hint message in control attribute
                                                var newHint = ORBEON.util.Dom.getAttribute(controlElement, "hint");
                                                if (newHint != null)
                                                    ORBEON.xforms.Controls.setHintMessage(documentElement, newHint);
                                                // Store new help message in control attribute
                                                var newHelp = ORBEON.util.Dom.getAttribute(controlElement, "help");
                                                if (newHelp != null)
                                                    ORBEON.xforms.Controls.setHelpMessage(documentElement, newHelp);
                                                // Store new alert message in control attribute
                                                var newAlert = ORBEON.util.Dom.getAttribute(controlElement, "alert");
                                                if (newAlert != null)
                                                    ORBEON.xforms.Controls.setAlertMessage(documentElement, newAlert);
                                                // Store validity, label, hint, help in element
                                                var newValid = ORBEON.util.Dom.getAttribute(controlElement, "valid");
                                                if (newValid != null) {
                                                    var newIsValid = newValid != "false";
                                                    ORBEON.xforms.Controls.setValid(documentElement, newIsValid);
                                                }

                                                // After we update classes on textarea, copy those classes on the FCKeditor iframe
                                                if (ORBEON.util.Dom.hasClass(documentElement, "xforms-textarea")
                                                        && ORBEON.util.Dom.hasClass(documentElement, "xforms-mediatype-text-html")) {
                                                    ORBEON.xforms.Controls.updateHTMLAreaClasses(documentElement);
                                                }

                                                break;
                                            }

                                            // Model item properties on a repeat item
                                            case "repeat-iteration": {
                                                // Extract data from server response
                                                var repeatIterationElement = controlValuesElement.childNodes[j];
                                                var repeatId = ORBEON.util.Dom.getAttribute(repeatIterationElement, "id");
                                                var iteration = ORBEON.util.Dom.getAttribute(repeatIterationElement, "iteration");
                                                var relevant = ORBEON.util.Dom.getAttribute(repeatIterationElement, "relevant");
                                                // Remove or add xforms-disabled on elements after this delimiter
                                                var cursor = xformsFindRepeatDelimiter(repeatId, iteration).nextSibling;
                                                while (!(cursor.nodeType == ELEMENT_TYPE &&
                                                         (ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-delimiter")
                                                            || ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-begin-end")))) {
                                                    if (cursor.nodeType == ELEMENT_TYPE) {
                                                        if (relevant) {
                                                            if (relevant == "true") ORBEON.util.Dom.removeClass(cursor, "xforms-disabled");
                                                            else ORBEON.util.Dom.addClass(cursor, "xforms-disabled");
                                                        }
                                                    }
                                                    cursor = cursor.nextSibling;
                                                }
                                                break;
                                            }
                                        }
                                    }
                                    break;
                                }

                                // Display or hide divs
                                case "divs": {
                                    var divsElement = actionElement.childNodes[actionIndex];
                                    for (var j = 0; j < divsElement.childNodes.length; j++) {
                                        if (xformsGetLocalName(divsElement.childNodes[j]) == "div") {
                                            var divElement = divsElement.childNodes[j];
                                            var controlId = ORBEON.util.Dom.getAttribute(divElement, "id");
                                            var visibile = ORBEON.util.Dom.getAttribute(divElement, "visibility") == "visible";

                                            var yuiDialog = ORBEON.xforms.Globals.dialogs[controlId];
                                            if (yuiDialog == null) {
                                                // This is a case
                                                var caseBeginId = "xforms-case-begin-" + controlId;
                                                var caseBegin = ORBEON.util.Dom.getElementById(caseBeginId);
                                                var caseBeginParent = caseBegin.parentNode;
                                                var foundCaseBegin = false;
                                                for (var childId = 0; caseBeginParent.childNodes.length; childId++) {
                                                    var cursor = caseBeginParent.childNodes[childId];
                                                    if (!foundCaseBegin) {
                                                        if (cursor.id == caseBegin.id) foundCaseBegin = true;
                                                        else continue;
                                                    }
                                                    if (cursor.nodeType == ELEMENT_TYPE) {
                                                        if (cursor.id == "xforms-case-end-" + controlId) break;
                                                        ORBEON.util.Dom.addClass(cursor, visibile ? "xforms-case-selected" : "xforms-case-deselected");
                                                        ORBEON.util.Dom.removeClass(cursor, visibile ? "xforms-case-deselected" : "xforms-case-selected");
                                                    }
                                                }
                                            } else {
                                                // This is a dialog
                                                if (visibile)  {
                                                    // Fixes cursor Firefox issue; more on this in dialog init code
                                                    yuiDialog.element.style.display = "block";
                                                    // Show the dialog
                                                    yuiDialog.show();
                                                    // By default try to display the dialog inside the viewport, but this can be overridden with consrain="false"
                                                    var constrain = ORBEON.util.Dom.getAttribute(divElement, "constrain") == "false" ? false : true;
                                                    yuiDialog.cfg.setProperty("constraintoviewport", constrain);
                                                    // Position the dialog either at the center of the viewport or relative of a neighbor
                                                    var neighbor = ORBEON.util.Dom.getAttribute(divElement, "neighbor");
                                                    if (neighbor == null) {
                                                        // Center dialog in page, if not positined relative to other element
                                                        yuiDialog.center();
                                                    } else {
                                                        // Align dialog relative to neighbor
                                                        yuiDialog.cfg.setProperty("context", [neighbor, "tl", "bl"]);
                                                        yuiDialog.align();
                                                        ORBEON.xforms.Globals.dialogMinimalVisible[controlId] = true;
                                                    }
                                                    // Take out the focus from the current control. This is particulary important with non-modal dialogs
                                                    // opened with a minimal trigger, otherwise we have a dotted line around the link after it opens.
                                                    if (ORBEON.xforms.Globals.lastFocusControlId) {
                                                        var focusedElement = ORBEON.util.Dom.getElementById(ORBEON.xforms.Globals.lastFocusControlId);
                                                        if (focusedElement) focusedElement.blur();
                                                    }
                                                } else {
                                                    yuiDialog.hide();
                                                    // Fixes cursor Firefox issue; more on this in dialog init code
                                                    yuiDialog.element.style.display = "none";
                                                    // Remember the server knows that this dialog is closed so we don't close it again later
                                                    if (ORBEON.xforms.Globals.dialogMinimalVisible[yuiDialog.element.id])
                                                        ORBEON.xforms.Globals.dialogMinimalVisible[yuiDialog.element.id] = false;
                                                }
                                            }
                                        }
                                    }

                                    // After we display divs, we must reenable the HTML editors.
                                    // This is a workaround for a Gecko bug documented at:
                                    // http://wiki.fckeditor.net/Troubleshooting#gecko_hidden_div
                                    if (XFORMS_IS_GECKO && ORBEON.xforms.Globals.htmlAreaNames.length > 0) {
                                        for (var htmlAreaIndex = 0; htmlAreaIndex < ORBEON.xforms.Globals.htmlAreaNames.length; htmlAreaIndex++) {
                                            var name = ORBEON.xforms.Globals.htmlAreaNames[htmlAreaIndex];
                                            var editor = FCKeditorAPI.GetInstance(name);
                                            try {
                                                editor.EditorDocument.designMode = "on";
                                            } catch (e) {
                                                // Nop
                                            }
                                        }
                                    }

                                    break;
                                }

                                // Change highlighted section in repeat
                                case "repeat-indexes": {
                                    var repeatIndexesElement = actionElement.childNodes[actionIndex];
                                    var newRepeatIndexes = new Array();
                                    // Extract data from server response
                                    for (var j = 0; j < repeatIndexesElement.childNodes.length; j++) {
                                        if (xformsGetLocalName(repeatIndexesElement.childNodes[j]) == "repeat-index") {
                                            var repeatIndexElement = repeatIndexesElement.childNodes[j];
                                            var repeatId = ORBEON.util.Dom.getAttribute(repeatIndexElement, "id");
                                            var newIndex = ORBEON.util.Dom.getAttribute(repeatIndexElement, "new-index");
                                            newRepeatIndexes[repeatId] = newIndex;
                                        }
                                    }
                                    // For each repeat id that changes, see if all the children are also included in
                                    // newRepeatIndexes. If they are not, add an entry with the index unchanged.
                                    for (var repeatId in newRepeatIndexes) {
                                        if (typeof repeatId == "string") { // hack because repeatId may be trash when some libraries override Object
                                            var children = ORBEON.xforms.Globals.repeatTreeParentToAllChildren[repeatId];
                                            if (children != null) { // test on null is a hack because repeatId may be trash when some libraries override Object
                                                for (var childIndex in children) {
                                                    var child = children[childIndex];
                                                    if (!newRepeatIndexes[child])
                                                        newRepeatIndexes[child] = ORBEON.xforms.Globals.repeatIndexes[child];
                                                }
                                            }
                                        }
                                    }
                                    // Unhighlight items at old indexes
                                    for (var repeatId in newRepeatIndexes) {
                                        if (typeof repeatId == "string") { // hack because repeatId may be trash when some libraries override Object
                                            var oldIndex = ORBEON.xforms.Globals.repeatIndexes[repeatId];
                                            if (typeof oldIndex == "string" && oldIndex != 0) { // hack because repeatId may be trash when some libraries override Object
                                                var oldItemDelimiter = xformsFindRepeatDelimiter(repeatId, oldIndex);
                                                if (oldItemDelimiter != null) {
                                                    var cursor = oldItemDelimiter.nextSibling;
                                                    while (cursor.nodeType != ELEMENT_TYPE ||
                                                           (!ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-delimiter")
                                                           && !ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-begin-end"))) {
                                                        if (cursor.nodeType == ELEMENT_TYPE)
                                                            ORBEON.util.Dom.removeClass(cursor, xformsGetClassForRepeatId(repeatId));
                                                        cursor = cursor.nextSibling;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    // Store new indexes
                                    for (var repeatId in newRepeatIndexes) {
                                        var newIndex = newRepeatIndexes[repeatId];
                                        ORBEON.xforms.Globals.repeatIndexes[repeatId] = newIndex;
                                    }
                                    // Highlight item at new index
                                    for (var repeatId in newRepeatIndexes) {
                                        if (typeof repeatId == "string") { // hack because repeatId may be trash when some libraries override Object
                                            var newIndex = newRepeatIndexes[repeatId];
                                            if (typeof newIndex == "string" && newIndex != 0) { // hack because repeatId may be trash when some libraries override Object
                                                var newItemDelimiter = xformsFindRepeatDelimiter(repeatId, newIndex);
                                                var cursor = newItemDelimiter.nextSibling;
                                                while (cursor.nodeType != ELEMENT_TYPE ||
                                                       (!ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-delimiter")
                                                       && !ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-begin-end"))) {
                                                    if (cursor.nodeType == ELEMENT_TYPE)
                                                        ORBEON.util.Dom.addClass(cursor, xformsGetClassForRepeatId(repeatId));
                                                    cursor = cursor.nextSibling;
                                                }
                                            }
                                        }
                                    }
                                    break;
                                }

                                // Server events
                                case "server-events": {
                                    // If there is a "submission" element, this must always come first
                                    serverEventsIndex = actionIndex;
                                    break;
                                }

                                // Submit form
                                case "submission": {
                                    var submissionElement = actionElement.childNodes[actionIndex];
                                    var showProcess = ORBEON.util.Dom.getAttribute(submissionElement, "show-progress");
                                    var action = ORBEON.util.Dom.getAttribute(submissionElement, "action");
                                    var replace = ORBEON.util.Dom.getAttribute(submissionElement, "replace");
                                    if (replace == null) replace = "all";
                                    ORBEON.xforms.Globals.formDynamicState[formID].value = newDynamicState;
                                    if (serverEventsIndex != -1) {
                                        ORBEON.xforms.Globals.formServerEvents[formID].value = ORBEON.util.Dom.getStringValue(actionElement.childNodes[serverEventsIndex]);
                                    } else {
                                        ORBEON.xforms.Globals.formServerEvents[formID].value = "";
                                    }
                                    if (replace == "all") {
                                        // Go to another page
                                        if (showProcess != "false")
                                            // Display loading indicator unless the server tells us not to display it
                                            newDynamicStateTriggersReplace = true;
                                        // Reset these as they may be changed by asyncRequest!
                                        ORBEON.xforms.Globals.requestForm.action = action;
                                        ORBEON.xforms.Globals.requestForm.removeAttribute("target");
                                        ORBEON.xforms.Globals.requestForm.submit();
                                    } else {
                                        // Submit form in the background
                                        YAHOO.util.Connect.setForm(ORBEON.xforms.Globals.requestForm, true, true);
                                        var callback =  {
                                            upload: ORBEON.xforms.Server.handleUploadResponse,
                                            failure: ORBEON.xforms.Server.handleFailure
                                        }
                                        YAHOO.util.Connect.asyncRequest("POST", action, callback);
                                    }
                                    ORBEON.xforms.Globals.formServerEvents[formID].value = "";
                                    break;
                                }

                                // Display modal message
                                case "message": {
                                    var messageElement = actionElement.childNodes[actionIndex];
                                    var message = ORBEON.util.Dom.getStringValue(messageElement);
                                    if (ORBEON.util.Dom.getAttribute(messageElement, "level") == "modal")
                                        alert(message);
                                    break;
                                }

                                // Load another page
                                case "load": {
                                    var loadElement = actionElement.childNodes[actionIndex];
                                    var resource = ORBEON.util.Dom.getAttribute(loadElement, "resource");
                                    var show = ORBEON.util.Dom.getAttribute(loadElement, "show");
                                    var target = ORBEON.util.Dom.getAttribute(loadElement, "target");
                                    var showProcess = ORBEON.util.Dom.getAttribute(loadElement, "show-progress");
                                    if (show == "replace") {
                                        if (target == null) {
                                            // Display loading indicator unless the server tells us not to display it
                                            if (showProcess != "false")
                                                newDynamicStateTriggersReplace = true;
                                            window.location.href = resource;
                                        } else {
                                            window.open(resource, target);
                                        }
                                    } else {
                                        window.open(resource, "_blank");
                                    }
                                    break;
                                }

                                // Set focus to a control
                                case "setfocus": {
                                    var setfocusElement = actionElement.childNodes[actionIndex];
                                    var controlId = ORBEON.util.Dom.getAttribute(setfocusElement, "control-id");
                                    ORBEON.xforms.Controls.setFocus(controlId);
                                    break;
                                }

                                // Run JavaScript code
                                case "script": {
                                    var scriptElement = actionElement.childNodes[actionIndex];
                                    var functionName = ORBEON.util.Dom.getAttribute(scriptElement, "name");
                                    var targetId = ORBEON.util.Dom.getAttribute(scriptElement, "target-id");
                                    var observerId = ORBEON.util.Dom.getAttribute(scriptElement, "observer-id");
                                    ORBEON.xforms.Server.callUserScript(functionName, targetId, observerId);
                                    break;
                                }
                            }
                        }
                    }
                }

                if (newDynamicStateTriggersReplace) {
                    // Display loading indicator when we go to another page.
                    // Display it even if it was not displayed before as loading the page could take time.
                    xformsDisplayIndicator("loading");
                    ORBEON.xforms.Globals.loadingOtherPage = true;
                }

                // Hack for instance inspector - need to see how to do this better
//                sourceResize();

            } else if (responseXML && responseXML.documentElement
                    && responseXML.documentElement.tagName.indexOf("error") != -1) {
                // Find an error message starting from the inner-most exception
                var details = ORBEON.util.Dom.getStringValue(responseXML.documentElement);
                ORBEON.xforms.Server.showError(details, formID);
            } else {
                // The server didn't send valid XML
                ORBEON.xforms.Globals.lastRequestIsError = true;
                ORBEON.xforms.Server.showError("Server didn't respond with valid XML", formID);
            }
        } catch (e) {
            ORBEON.xforms.Server.exceptionWhenTalkingToServer(e, formID);
        }

        // Reset changes, as changes are included in this bach of events
        ORBEON.xforms.Globals.changedIdsRequest = {};

        // Go ahead with next request, if any
        ORBEON.xforms.Globals.requestInProgress = false;
        ORBEON.xforms.Globals.executeEventFunctionQueued++;
        ORBEON.xforms.Server.executeNextRequest(false);
    },

    callUserScript: function(functionName, targetId, observerId) {
        var targetElement = ORBEON.util.Dom.getElementById(targetId);
        var observer = ORBEON.util.Dom.getElementById(observerId);
        var event = { "target" : targetElement };
        var theFunction = eval(functionName);
        theFunction.call(observer, event);
    }
};

function xformsIsDefined(thing) {
    return typeof thing != "undefined";
}

function xformsDispatchEvent(target, eventName) {
    if (target.dispatchEvent) {
        var event = document.createEvent("HTMLEvents");
        event.initEvent(eventName.toLowerCase(), true, true);
        target.dispatchEvent(event);
    } else {
        target.fireEvent("on" + eventName);
    }
}

function xformsPreventDefault(event) {
    if (event.preventDefault) {
        // Firefox version
        event.preventDefault();
    } else {
        // IE version
        return false;
    }
}

function xformsArrayContains(array, element) {
    for (var i = 0; i < array.length; i++)
        if (array[i] == element)
            return true;
    return false;
}

function xformsStringReplaceWorker(node, placeholderRegExp, replacement) {
    switch (node.nodeType) {
        case ELEMENT_TYPE:
            for (var i = 0; i < node.attributes.length; i++) {
                var newValue = new String(node.attributes[i].value).replace(placeholderRegExp, replacement);
                if (newValue != node.attributes[i].value)
                    node.setAttribute(node.attributes[i].name, newValue);
            }
            for (var i = 0; i < node.childNodes.length; i++)
                xformsStringReplaceWorker(node.childNodes[i], placeholderRegExp, replacement);
            break;
        case TEXT_TYPE:
            var newValue = new String(node.nodeValue).replace(placeholderRegExp, replacement);
            if (newValue != node.nodeValue)
                node.nodeValue = newValue;
            break;
    }
}

// Replace in a tree a placeholder by some other string in text nodes and attribute values
function xformsStringReplace(node, placeholder, replacement) {
    var placeholderRegExp = new RegExp(placeholder.replace(new RegExp("\\$", "g"), "\\$"), "g");
    xformsStringReplaceWorker(node, placeholderRegExp, replacement);
}

function xformsNormalizeEndlines(text) {
    return text.replace(XFORMS_REGEXP_CR, "");
}

function xformsAppendRepeatSuffix(id, suffix) {
    if (suffix == "")
        return id;
    if (suffix.charAt(0) == XFORMS_SEPARATOR_2)
        suffix = suffix.substring(1);
    if (id.indexOf(XFORMS_SEPARATOR_1) == -1)
        return id + XFORMS_SEPARATOR_1 + suffix;
    else
        return id + XFORMS_SEPARATOR_2 + suffix;
}

/**
* Locate the delimiter at the given position starting from a repeat begin element.
*/
function xformsFindRepeatDelimiter(repeatId, index) {

    // Find id of repeat begin for the current repeatId
    var parentRepeatIndexes = "";
    {
        var currentId = repeatId;
        while (true) {
            var parent = ORBEON.xforms.Globals.repeatTreeChildToParent[currentId];
            if (parent == null) break;
            var grandParent = ORBEON.xforms.Globals.repeatTreeChildToParent[parent];
            parentRepeatIndexes = (grandParent == null ? XFORMS_SEPARATOR_1 : XFORMS_SEPARATOR_2)
                    + ORBEON.xforms.Globals.repeatIndexes[parent] + parentRepeatIndexes;
            currentId = parent;
        }
    }

    var beginElementId = "repeat-begin-" + repeatId + parentRepeatIndexes;
    var beginElement = ORBEON.util.Dom.getElementById(beginElementId);
    if (!beginElement) return null;
    var cursor = beginElement;
    var cursorPosition = 0;
    while (true) {
        while (cursor.nodeType != ELEMENT_TYPE || !ORBEON.util.Dom.hasClass(cursor, "xforms-repeat-delimiter")) {
            cursor = cursor.nextSibling;
            if (!cursor) return null;
        }
        cursorPosition++;
        if (cursorPosition == index) break;
        cursor = cursor.nextSibling;
    }

    return cursor;
}

function xformsLog(object) {
    var debugDiv = ORBEON.util.Dom.getElementById("xforms-debug");
    if (debugDiv == null) {
        // Figure out width and heigh of visible part of the page
        var visibleWidth;
        var visibleHeight;
        if (navigator.appName.indexOf("Microsoft")!=-1) {
            visibleWidth = document.body.offsetWidth;
            visibleHeight = document.body.offsetHeight;
        } else {
            visibleWidth = window.innerWidth;
            visibleHeight = window.innerHeight;
        }

        // Create div with appropriate size and position
        debugDiv = document.createElement("div");
        debugDiv.className = "xforms-debug";
        debugDiv.id = "xforms-debug";
        debugDiv.style.width = XFORMS_DEBUG_WINDOW_WIDTH + "px";
        debugDiv.style.left = visibleWidth - (XFORMS_DEBUG_WINDOW_WIDTH + 50) + "px";
        debugDiv.style.height = XFORMS_DEBUG_WINDOW_HEIGHT + "px";
        debugDiv.style.top = visibleHeight - (XFORMS_DEBUG_WINDOW_HEIGHT + 20) + "px";

        // Add "clear" button
        var clear = document.createElement("BUTTON");
        clear.appendChild(document.createTextNode("Clear"));
        debugDiv.appendChild(clear);
        document.body.insertBefore(debugDiv, document.body.firstChild);

        // Handle click on clear button
        YAHOO.util.Event.addListener(clear, "click", function (event) {
            var target = getEventTarget(event);
            while (target.nextSibling)
                target.parentNode.removeChild(target.nextSibling);
            return false;
        });

        // Make it so user can move the debug window
        YAHOO.util.Event.addListener(debugDiv, "mousedown", function (event) {
            ORBEON.xforms.Globals.debugDiv = getEventTarget(event);
            return false;
        });
        YAHOO.util.Event.addListener(document, "mouseup", function (event) {
            ORBEON.xforms.Globals.debugDiv = null;
            return false;
        });
        YAHOO.util.Event.addListener(document, "mousemove", function (event) {
            if (ORBEON.xforms.Globals.debugDiv) {
                ORBEON.xforms.Globals.debugDiv.style.left = event.clientX;
                ORBEON.xforms.Globals.debugDiv.style.top = event.clientY;
            }
            return false;
        });
    }
    // Convert object to text
    text =
        object === undefined ? "undefined"
        : object === null ? "null"
        : typeof object == "string" && object == "" ? "empty string"
        : object.nodeType && object.nodeType == ORBEON.util.Dom.ELEMENT_TYPE ? "Element " + object.tagName
        : object.nodeType && object.nodeType == ORBEON.util.Dom.TEXT_TYPE ? "Text: " + ORBEON.util.Dom.getStringValue(object)
        : object;
    debugDiv.innerHTML += text + " | ";
}

function xformsLogTime(text) {
    var oldTime = ORBEON.xforms.Globals.debugLastTime;
    var currentTime = new Date().getTime();
    ORBEON.xforms.Globals.debugLastTime = currentTime;
    xformsLog((currentTime - oldTime) + ": " + text);
}

function xformsLogProperties(object) {
    var message = "[";
    var first = true;
    for (var p in object) {
        if (first) first = false; else message += ", ";
        message += p + ": " + object[p];
    }
    message += "]";
    xformsLog(message);
}

function xformsDisplayIndicator(state) {
    var form = ORBEON.xforms.Globals.requestForm;
    var formID = form.id;
    switch (state) {
        case "loading":
            if (ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID] != null) {
                ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID].cfg.setProperty("visible", true);
                ORBEON.xforms.Controls.updateLoadingPosition(formID);
            }
            if (ORBEON.xforms.Globals.formLoadingNone[formID] != null)
                ORBEON.xforms.Globals.formLoadingNone[formID].style.display = "block";
            break;
        case "none":
            if (!ORBEON.xforms.Globals.loadingOtherPage) {
                if (ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID] != null)
                    ORBEON.xforms.Globals.formLoadingLoadingOverlay[formID].cfg.setProperty("visible", false);
                if (ORBEON.xforms.Globals.formLoadingNone[formID] != null)
                    ORBEON.xforms.Globals.formLoadingNone[formID].style.display = "block";
            }
            break;
    }
}

// Gets a value stored in the hidden client-state input field
function xformsGetFromClientState(formID, key) {
    var clientState = ORBEON.xforms.Globals.formClientState[formID];
    var keyValues = clientState.value.split("&");
    for (var i = 0; i < keyValues.length; i = i + 2)
        if (keyValues[i] == key)
            return unescape(keyValues[i + 1]);
    return null;
}

// Returns a value stored in the hidden client-state input field
function xformsStoreInClientState(formID, key, value) {
    var clientState = ORBEON.xforms.Globals.formClientState[formID];
    var keyValues = clientState.value == ""? new Array() : clientState.value.split("&");
    var found = false;
    // If we found the key, replace the value
    for (var i = 0; i < keyValues.length; i = i + 2) {
        if (keyValues[i] == key) {
            keyValues[i + 1] = escape(value);
            found = true;
            break;
        }
    }
    // If key is there already, replace it
    if (!found) {
        keyValues.push(key);
        keyValues.push(escape(value));
    }
    clientState.value = keyValues.join("&");
}

/**
 * Value change handling for all the controls. It is assumed that the "value" property of "target"
 * contain the current value for the control. We create a value change event and fire it by calling
 * xformsFireEvents().
 *
 * This function is in general called by xformsHandleValueChange(), and will be called directly by
 * other event handler for less usual events (e.g. slider, HTML area).
 */
function xformsValueChanged(target, other) {
    var valueChanged = target.value != target.previousValue;
    // We don't send value change events for the XForms upload control
    var isUploadControl = ORBEON.util.Dom.hasClass(target, "xforms-upload");
    if (valueChanged && !isUploadControl) {
        target.previousValue = target.value;
        var events = new Array(xformsCreateEventArray
                (target, "xxforms-value-change-with-focus-change", target.value, other));
        var incremental = other == null
                && ORBEON.util.Dom.hasClass(target, "xforms-incremental");
        xformsFireEvents(events, incremental);
    }
    return valueChanged;
}

// Handle click on trigger
function xformsHandleClick(event) {
    var target = getEventTarget(event);
    // Make sure the user really clicked on the trigger, instead of pressing enter in a nearby control
    if ((ORBEON.util.Dom.hasClass(target, "xforms-trigger") || ORBEON.util.Dom.hasClass(target, "xforms-trigger"))
            && !ORBEON.util.Dom.hasClass(target, "xforms-readonly"))
        xformsFireEvents(new Array(xformsCreateEventArray(target, "DOMActivate", null)), false);
    return false;
}

function xformsHandleAutoCompleteMouseChange(input) {
    input.parentNode.lastKeyCode = -1;
    input.parentNode.value = input.value;
    xformsValueChanged(input.parentNode, null);
}

/**
 * Root function called by any widget that wants an event to be sent to the server.
 *
 * @param events       Array of arrays with each array containing:
 *                     new Array(target, eventName, value, other)
 * @param incremental  Are these incremental events
 */
function xformsFireEvents(events, incremental) {
    var newEvents = [];
    for (var eventIndex = 0; eventIndex < events.length; eventIndex++) {
        var event = events[eventIndex];
        var target = event[0];
        var eventName = event[1];
        var value = event[2];
        var other = event[3];
        newEvents.push(new ORBEON.xforms.Server.Event(
                target == undefined ? null : ORBEON.xforms.Controls.getForm(target),
                target == undefined ? null : target.id,
                other == undefined ? null : other.id,
                value, eventName, null, null));
    }
    ORBEON.xforms.Server.fireEvents(newEvents, incremental);
}

function xformsCreateEventArray(target, eventName, value, other) {
    return new Array(target, eventName, value, other);
}

function getEventTarget(event) {
    if (event && event.LinkedField) {
        // Case of events coming from HTML area thrown by the HTML area library
        return event.LinkedField;
    } else {
        // Case of normal HTML DOM events
        event = event ? event : window.event;
        var target = event.srcElement ? event.srcElement : event.target;
        if (target.xformsElement) {
            // HTML area on Gecko: event target is the document, return the textarea
            return target.xformsElement;
        } else if (target.ownerDocument.xformsElement) {
            // HTML area on IS: event target is the body of the document, return the textarea
            return target.ownerDocument.xformsElement;
        } else {
            return target;
        }
    }
}

function xformsHtmlEditorChange(editorInstance) {
    editorInstance.LinkedField.value = editorInstance.GetXHTML();
    // Throw value change event if the field is in incremental mode
    if (ORBEON.util.Dom.hasClass(editorInstance.LinkedField, "xforms-incremental"))
        xformsValueChanged(editorInstance.LinkedField, null);
}

/**
 * Called by FCKeditor when an editor is fully loaded. This is our opportunity
 * to listen for events on this editor.
 */
function FCKeditor_OnComplete(editorInstance) {
    // Save reference to XForms element (textarea) in document for event handlers that receive the document
    editorInstance.EditorDocument.xformsElement = editorInstance.LinkedField;
    // Register value change handler when in incremental mode
    if (ORBEON.util.Dom.hasClass(editorInstance.LinkedField, "xforms-incremental"))
        editorInstance.Events.AttachEvent("OnSelectionChange", xformsHtmlEditorChange);
    // Register focus/blur events for Gecko
    YAHOO.util.Event.addListener(editorInstance.EditorDocument, "focus", ORBEON.xforms.Events.focus);
    YAHOO.util.Event.addListener(editorInstance.EditorDocument, "blur", ORBEON.xforms.Events.blur);
    // Register focus/blur events for IE
    YAHOO.util.Event.addListener(editorInstance.EditorDocument, "focusin", ORBEON.xforms.Events.focus);
    YAHOO.util.Event.addListener(editorInstance.EditorDocument, "focusout", ORBEON.xforms.Events.blur);
    // Load other editors in the queue
    if (ORBEON.xforms.Globals.fckEditorsToLoad.length > 0) {
        var fckEditor = ORBEON.xforms.Globals.fckEditorsToLoad.shift();
        fckEditor.ReplaceTextarea();
        ORBEON.xforms.Controls.updateHTMLAreaClasses(ORBEON.util.Dom.getElementById(fckEditor.InstanceName));
    } else {
        ORBEON.xforms.Globals.fckEditorLoading = false;
        if (typeof xformsPageLoadedServer != "undefined")
            xformsPageLoadedServer();
    }
}

function xformsGetLocalName(element) {
    if (element.nodeType == 1) {
        return element.tagName.indexOf(":") == -1
            ? element.tagName
            : element.tagName.substr(element.tagName.indexOf(":") + 1);
    } else {
        return null;
    }
}

function xformsAddSuffixToIds(element, idSuffix, repeatDepth) {
    var idSuffixWithDepth = idSuffix;
    for (var repeatDepthIndex = 0; repeatDepthIndex < repeatDepth; repeatDepthIndex++)
         idSuffixWithDepth += XFORMS_SEPARATOR_2 + "1";
    if (element.id)
        element.id = xformsAppendRepeatSuffix(element.id, idSuffixWithDepth);
    if (element.htmlFor)
        element.htmlFor = xformsAppendRepeatSuffix(element.htmlFor, idSuffixWithDepth);
    if (element.name) {
        var newName = xformsAppendRepeatSuffix(element.name, idSuffixWithDepth);
        if (element.tagName.toLowerCase() == "input" && element.type.toLowerCase() == "radio"
                && ORBEON.xforms.Globals.isRenderingEngineTridend) {
            // IE supports changing the name of elements, but according to the Microsoft documentation, "This does not
            // cause the name in the programming model to change in the collection of elements". This has a implication
            // for radio buttons where using a same name for a set of radio buttons is used to group them together.
            // http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/properties/name_2.asp
            var clone = document.createElement("<" + element.tagName + " name='" + newName + "'>");
            for (var attributeIndex = 0; attributeIndex < element.attributes.length; attributeIndex++) {
                var attribute = element.attributes[attributeIndex];
                if (attribute.nodeName.toLowerCase() != "name" && attribute.nodeName.toLowerCase() != "height" && attribute.nodeValue)
                    clone.setAttribute(attribute.nodeName, attribute.nodeValue);
            }
            YAHOO.util.Event.addListener(clone, "focus", ORBEON.xforms.Events.focus);
            YAHOO.util.Event.addListener(clone, "blur", ORBEON.xforms.Events.blur);
            YAHOO.util.Event.addListener(clone, "change", ORBEON.xforms.Events.change);
            element.replaceNode(clone);
        } else {
            element.name = newName;
        }
    }
    // Remove references to hint, help, alert, label as they might have changed
    for (var childIndex = 0; childIndex < element.childNodes.length; childIndex++) {
        var childNode = element.childNodes[childIndex];
        if (childNode.nodeType == ELEMENT_TYPE) {
            if (childNode.id && childNode.id.indexOf("repeat-end-") == 0) repeatDepth--;
            xformsAddSuffixToIds(childNode, idSuffix, repeatDepth);
            if (childNode.id && childNode.id.indexOf("repeat-begin-") == 0) repeatDepth++
        }
    }
}

// Function to find the input element below a given node
function xformsGetInputUnderNode(node) {
    if (node.nodeType == ELEMENT_TYPE) {
        if (node.tagName.toLowerCase() == "input") {
            return node;
        } else {
            for (var childIndex = 0; childIndex < node.childNodes.length; childIndex++) {
                var result = xformsGetInputUnderNode(node.childNodes[childIndex]);
                if (result != null) return result;
            }
        }
    } else {
        return null;
    }
}

function xformsGetClassForRepeatId(repeatId) {
    var depth = 1;
    var currentRepeatId = repeatId;
    while (true) {
        currentRepeatId = ORBEON.xforms.Globals.repeatTreeChildToParent[currentRepeatId];
        if (currentRepeatId == null) break;
        depth = (depth == 4) ? 1 : depth + 1;
    }
    return "xforms-repeat-selected-item-" + depth;
}

function xformsDisplayLoading() {
    if (ORBEON.xforms.Globals.requestInProgress == true)
        xformsDisplayIndicator("loading");
}

// Run xformsPageLoaded when the browser has finished loading the page
// In case this script is loaded twice, we still want to run the initialization only once
if (!ORBEON.xforms.Globals.pageLoadedRegistered) {
    ORBEON.xforms.Globals.pageLoadedRegistered = true;
    // If the browser does not provide a console object, create one which delegates log() to xformsLog()
    YAHOO.util.Event.addListener(window, "load", ORBEON.xforms.Init.document);
    ORBEON.xforms.Globals.debugLastTime = new Date().getTime();
}
