//MooTools, My Object Oriented Javascript Tools. Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.

var MooTools={version:"1.11"};function $defined(A){return(A!=undefined);}function $type(B){if(!$defined(B)){return false;}if(B.htmlElement){return"element";
}var A=typeof B;if(A=="object"&&B.nodeName){switch(B.nodeType){case 1:return"element";case 3:return(/\S/).test(B.nodeValue)?"textnode":"whitespace";}}if(A=="object"||A=="function"){switch(B.constructor){case Array:return"array";
case RegExp:return"regexp";case Class:return"class";}if(typeof B.length=="number"){if(B.item){return"collection";}if(B.callee){return"arguments";}}}return A;
}function $merge(){var C={};for(var B=0;B<arguments.length;B++){for(var E in arguments[B]){var A=arguments[B][E];var D=C[E];if(D&&$type(A)=="object"&&$type(D)=="object"){C[E]=$merge(D,A);
}else{C[E]=A;}}}return C;}var $extend=function(){var A=arguments;if(!A[1]){A=[this,A[0]];}for(var B in A[1]){A[0][B]=A[1][B];}return A[0];};var $native=function(){for(var B=0,A=arguments.length;
B<A;B++){arguments[B].extend=function(C){for(var D in C){if(!this.prototype[D]){this.prototype[D]=C[D];}if(!this[D]){this[D]=$native.generic(D);}}};}};
$native.generic=function(A){return function(B){return this.prototype[A].apply(B,Array.prototype.slice.call(arguments,1));};};$native(Function,Array,String,Number);
function $chk(A){return !!(A||A===0);}function $pick(B,A){return $defined(B)?B:A;}function $random(B,A){return Math.floor(Math.random()*(A-B+1)+B);}function $time(){return new Date().getTime();
}function $clear(A){clearTimeout(A);clearInterval(A);return null;}var Abstract=function(A){A=A||{};A.extend=$extend;return A;};var Window=new Abstract(window);
var Document=new Abstract(document);document.head=document.getElementsByTagName("head")[0];window.xpath=!!(document.evaluate);if(window.ActiveXObject){window.ie=window[window.XMLHttpRequest?"ie7":"ie6"]=true;
}else{if(document.childNodes&&!document.all&&!navigator.taintEnabled){window.webkit=window[window.xpath?"webkit420":"webkit419"]=true;}else{if(document.getBoxObjectFor!=null){window.gecko=true;
}}}window.khtml=window.webkit;Object.extend=$extend;if(typeof HTMLElement=="undefined"){var HTMLElement=function(){};if(window.webkit){document.createElement("iframe");
}HTMLElement.prototype=(window.webkit)?window["[[DOMElement.prototype]]"]:{};}HTMLElement.prototype.htmlElement=function(){};if(window.ie6){try{document.execCommand("BackgroundImageCache",false,true);
}catch(e){}}var Class=function(B){var A=function(){return(arguments[0]!==null&&this.initialize&&$type(this.initialize)=="function")?this.initialize.apply(this,arguments):this;
};$extend(A,this);A.prototype=B;A.constructor=Class;return A;};Class.empty=function(){};Class.prototype={extend:function(B){var C=new this(null);for(var D in B){var A=C[D];
C[D]=Class.Merge(A,B[D]);}return new Class(C);},implement:function(){for(var B=0,A=arguments.length;B<A;B++){$extend(this.prototype,arguments[B]);}}};Class.Merge=function(C,D){if(C&&C!=D){var B=$type(D);
if(B!=$type(C)){return D;}switch(B){case"function":var A=function(){this.parent=arguments.callee.parent;return D.apply(this,arguments);};A.parent=C;return A;
case"object":return $merge(C,D);}}return D;};var Chain=new Class({chain:function(A){this.chains=this.chains||[];this.chains.push(A);return this;},callChain:function(){if(this.chains&&this.chains.length){this.chains.shift().delay(10,this);
}},clearChain:function(){this.chains=[];}});var Events=new Class({addEvent:function(B,A){if(A!=Class.empty){this.$events=this.$events||{};this.$events[B]=this.$events[B]||[];
this.$events[B].include(A);}return this;},fireEvent:function(C,B,A){if(this.$events&&this.$events[C]){this.$events[C].each(function(D){D.create({bind:this,delay:A,"arguments":B})();
},this);}return this;},removeEvent:function(B,A){if(this.$events&&this.$events[B]){this.$events[B].remove(A);}return this;}});var Options=new Class({setOptions:function(){this.options=$merge.apply(null,[this.options].extend(arguments));
if(this.addEvent){for(var A in this.options){if($type(this.options[A]=="function")&&(/^on[A-Z]/).test(A)){this.addEvent(A,this.options[A]);}}}return this;
}});Array.extend({forEach:function(C,D){for(var B=0,A=this.length;B<A;B++){C.call(D,this[B],B,this);}},filter:function(D,E){var C=[];for(var B=0,A=this.length;
B<A;B++){if(D.call(E,this[B],B,this)){C.push(this[B]);}}return C;},map:function(D,E){var C=[];for(var B=0,A=this.length;B<A;B++){C[B]=D.call(E,this[B],B,this);
}return C;},every:function(C,D){for(var B=0,A=this.length;B<A;B++){if(!C.call(D,this[B],B,this)){return false;}}return true;},some:function(C,D){for(var B=0,A=this.length;
B<A;B++){if(C.call(D,this[B],B,this)){return true;}}return false;},indexOf:function(C,D){var A=this.length;for(var B=(D<0)?Math.max(0,A+D):D||0;B<A;B++){if(this[B]===C){return B;
}}return -1;},copy:function(D,C){D=D||0;if(D<0){D=this.length+D;}C=C||(this.length-D);var A=[];for(var B=0;B<C;B++){A[B]=this[D++];}return A;},remove:function(C){var B=0;
var A=this.length;while(B<A){if(this[B]===C){this.splice(B,1);A--;}else{B++;}}return this;},contains:function(A,B){return this.indexOf(A,B)!=-1;},associate:function(C){var D={},B=Math.min(this.length,C.length);
for(var A=0;A<B;A++){D[C[A]]=this[A];}return D;},extend:function(C){for(var B=0,A=C.length;B<A;B++){this.push(C[B]);}return this;},merge:function(C){for(var B=0,A=C.length;
B<A;B++){this.include(C[B]);}return this;},include:function(A){if(!this.contains(A)){this.push(A);}return this;},getRandom:function(){return this[$random(0,this.length-1)]||null;
},getLast:function(){return this[this.length-1]||null;}});Array.prototype.each=Array.prototype.forEach;Array.each=Array.forEach;function $A(A){return Array.copy(A);
}function $each(C,B,D){if(C&&typeof C.length=="number"&&$type(C)!="object"){Array.forEach(C,B,D);}else{for(var A in C){B.call(D||C,C[A],A);}}}Array.prototype.test=Array.prototype.contains;
String.extend({test:function(A,B){return(($type(A)=="string")?new RegExp(A,B):A).test(this);},toInt:function(){return parseInt(this,10);},toFloat:function(){return parseFloat(this);
},camelCase:function(){return this.replace(/-\D/g,function(A){return A.charAt(1).toUpperCase();});},hyphenate:function(){return this.replace(/\w[A-Z]/g,function(A){return(A.charAt(0)+"-"+A.charAt(1).toLowerCase());
});},capitalize:function(){return this.replace(/\b[a-z]/g,function(A){return A.toUpperCase();});},trim:function(){return this.replace(/^\s+|\s+$/g,"");
},clean:function(){return this.replace(/\s{2,}/g," ").trim();},rgbToHex:function(B){var A=this.match(/\d{1,3}/g);return(A)?A.rgbToHex(B):false;},hexToRgb:function(B){var A=this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
return(A)?A.slice(1).hexToRgb(B):false;},contains:function(A,B){return(B)?(B+this+B).indexOf(B+A+B)>-1:this.indexOf(A)>-1;},escapeRegExp:function(){return this.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1");
}});Array.extend({rgbToHex:function(D){if(this.length<3){return false;}if(this.length==4&&this[3]==0&&!D){return"transparent";}var B=[];for(var A=0;A<3;
A++){var C=(this[A]-0).toString(16);B.push((C.length==1)?"0"+C:C);}return D?B:"#"+B.join("");},hexToRgb:function(C){if(this.length!=3){return false;}var A=[];
for(var B=0;B<3;B++){A.push(parseInt((this[B].length==1)?this[B]+this[B]:this[B],16));}return C?A:"rgb("+A.join(",")+")";}});Function.extend({create:function(A){var B=this;
A=$merge({bind:B,event:false,"arguments":null,delay:false,periodical:false,attempt:false},A);if($chk(A.arguments)&&$type(A.arguments)!="array"){A.arguments=[A.arguments];
}return function(E){var C;if(A.event){E=E||window.event;C=[(A.event===true)?E:new A.event(E)];if(A.arguments){C.extend(A.arguments);}}else{C=A.arguments||arguments;
}var F=function(){return B.apply($pick(A.bind,B),C);};if(A.delay){return setTimeout(F,A.delay);}if(A.periodical){return setInterval(F,A.periodical);}if(A.attempt){try{return F();
}catch(D){return false;}}return F();};},pass:function(A,B){return this.create({"arguments":A,bind:B});},attempt:function(A,B){return this.create({"arguments":A,bind:B,attempt:true})();
},bind:function(B,A){return this.create({bind:B,"arguments":A});},bindAsEventListener:function(B,A){return this.create({bind:B,event:true,"arguments":A});
},delay:function(B,C,A){return this.create({delay:B,bind:C,"arguments":A})();},periodical:function(A,C,B){return this.create({periodical:A,bind:C,"arguments":B})();
}});Number.extend({toInt:function(){return parseInt(this);},toFloat:function(){return parseFloat(this);},limit:function(B,A){return Math.min(A,Math.max(B,this));
},round:function(A){A=Math.pow(10,A||0);return Math.round(this*A)/A;},times:function(B){for(var A=0;A<this;A++){B(A);}}});var Element=new Class({initialize:function(D,C){if($type(D)=="string"){if(window.ie&&C&&(C.name||C.type)){var A=(C.name)?' name="'+C.name+'"':"";
var B=(C.type)?' type="'+C.type+'"':"";delete C.name;delete C.type;D="<"+D+A+B+">";}D=document.createElement(D);}D=$(D);return(!C||!D)?D:D.set(C);}});var Elements=new Class({initialize:function(A){return(A)?$extend(A,this):this;
}});Elements.extend=function(A){for(var B in A){this.prototype[B]=A[B];this[B]=$native.generic(B);}};function $(B){if(!B){return null;}if(B.htmlElement){return Garbage.collect(B);
}if([window,document].contains(B)){return B;}var A=$type(B);if(A=="string"){B=document.getElementById(B);A=(B)?"element":false;}if(A!="element"){return null;
}if(B.htmlElement){return Garbage.collect(B);}if(["object","embed"].contains(B.tagName.toLowerCase())){return B;}$extend(B,Element.prototype);B.htmlElement=function(){};
return Garbage.collect(B);}document.getElementsBySelector=document.getElementsByTagName;function $$(){var D=[];for(var C=0,B=arguments.length;C<B;C++){var A=arguments[C];
switch($type(A)){case"element":D.push(A);case"boolean":break;case false:break;case"string":A=document.getElementsBySelector(A,true);default:D.extend(A);
}}return $$.unique(D);}$$.unique=function(G){var D=[];for(var C=0,A=G.length;C<A;C++){if(G[C].$included){continue;}var B=$(G[C]);if(B&&!B.$included){B.$included=true;
D.push(B);}}for(var F=0,E=D.length;F<E;F++){D[F].$included=null;}return new Elements(D);};Elements.Multi=function(A){return function(){var D=arguments;
var B=[];var G=true;for(var E=0,C=this.length,F;E<C;E++){F=this[E][A].apply(this[E],D);if($type(F)!="element"){G=false;}B.push(F);}return(G)?$$.unique(B):B;
};};Element.extend=function(A){for(var B in A){HTMLElement.prototype[B]=A[B];Element.prototype[B]=A[B];Element[B]=$native.generic(B);var C=(Array.prototype[B])?B+"Elements":B;
Elements.prototype[C]=Elements.Multi(B);}};Element.extend({set:function(A){for(var C in A){var B=A[C];switch(C){case"styles":this.setStyles(B);break;case"events":if(this.addEvents){this.addEvents(B);
}break;case"properties":this.setProperties(B);break;default:this.setProperty(C,B);}}return this;},inject:function(C,A){C=$(C);switch(A){case"before":C.parentNode.insertBefore(this,C);
break;case"after":var B=C.getNext();if(!B){C.parentNode.appendChild(this);}else{C.parentNode.insertBefore(this,B);}break;case"top":var D=C.firstChild;if(D){C.insertBefore(this,D);
break;}default:C.appendChild(this);}return this;},injectBefore:function(A){return this.inject(A,"before");},injectAfter:function(A){return this.inject(A,"after");
},injectInside:function(A){return this.inject(A,"bottom");},injectTop:function(A){return this.inject(A,"top");},adopt:function(){var A=[];$each(arguments,function(B){A=A.concat(B);
});$$(A).inject(this);return this;},remove:function(){return this.parentNode.removeChild(this);},clone:function(C){var B=$(this.cloneNode(C!==false));if(!B.$events){return B;
}B.$events={};for(var A in this.$events){B.$events[A]={keys:$A(this.$events[A].keys),values:$A(this.$events[A].values)};}return B.removeEvents();},replaceWith:function(A){A=$(A);
this.parentNode.replaceChild(A,this);return A;},appendText:function(A){this.appendChild(document.createTextNode(A));return this;},hasClass:function(A){return this.className.contains(A," ");
},addClass:function(A){if(!this.hasClass(A)){this.className=(this.className+" "+A).clean();}return this;},removeClass:function(A){this.className=this.className.replace(new RegExp("(^|\\s)"+A+"(?:\\s|$)"),"$1").clean();
return this;},toggleClass:function(A){return this.hasClass(A)?this.removeClass(A):this.addClass(A);},setStyle:function(B,A){switch(B){case"opacity":return this.setOpacity(parseFloat(A));
case"float":B=(window.ie)?"styleFloat":"cssFloat";}B=B.camelCase();switch($type(A)){case"number":if(!["zIndex","zoom"].contains(B)){A+="px";}break;case"array":A="rgb("+A.join(",")+")";
}this.style[B]=A;return this;},setStyles:function(A){switch($type(A)){case"object":Element.setMany(this,"setStyle",A);break;case"string":this.style.cssText=A;
}return this;},setOpacity:function(A){if(A==0){if(this.style.visibility!="hidden"){this.style.visibility="hidden";}}else{if(this.style.visibility!="visible"){this.style.visibility="visible";
}}if(!this.currentStyle||!this.currentStyle.hasLayout){this.style.zoom=1;}if(window.ie){this.style.filter=(A==1)?"":"alpha(opacity="+A*100+")";}this.style.opacity=this.$tmp.opacity=A;
return this;},getStyle:function(C){C=C.camelCase();var A=this.style[C];if(!$chk(A)){if(C=="opacity"){return this.$tmp.opacity;}A=[];for(var B in Element.Styles){if(C==B){Element.Styles[B].each(function(F){var E=this.getStyle(F);
A.push(parseInt(E)?E:"0px");},this);if(C=="border"){var D=A.every(function(E){return(E==A[0]);});return(D)?A[0]:false;}return A.join(" ");}}if(C.contains("border")){if(Element.Styles.border.contains(C)){return["Width","Style","Color"].map(function(E){return this.getStyle(C+E);
},this).join(" ");}else{if(Element.borderShort.contains(C)){return["Top","Right","Bottom","Left"].map(function(E){return this.getStyle("border"+E+C.replace("border",""));
},this).join(" ");}}}if(document.defaultView){A=document.defaultView.getComputedStyle(this,null).getPropertyValue(C.hyphenate());}else{if(this.currentStyle){A=this.currentStyle[C];
}}}if(window.ie){A=Element.fixStyle(C,A,this);}if(A&&C.test(/color/i)&&A.contains("rgb")){return A.split("rgb").splice(1,4).map(function(E){return E.rgbToHex();
}).join(" ");}return A;},getStyles:function(){return Element.getMany(this,"getStyle",arguments);},walk:function(A,C){A+="Sibling";var B=(C)?this[C]:this[A];
while(B&&$type(B)!="element"){B=B[A];}return $(B);},getPrevious:function(){return this.walk("previous");},getNext:function(){return this.walk("next");},getFirst:function(){return this.walk("next","firstChild");
},getLast:function(){return this.walk("previous","lastChild");},getParent:function(){return $(this.parentNode);},getChildren:function(){return $$(this.childNodes);
},hasChild:function(A){return !!$A(this.getElementsByTagName("*")).contains(A);},getProperty:function(D){var B=Element.Properties[D];if(B){return this[B];
}var A=Element.PropertiesIFlag[D]||0;if(!window.ie||A){return this.getAttribute(D,A);}var C=this.attributes[D];return(C)?C.nodeValue:null;},removeProperty:function(B){var A=Element.Properties[B];
if(A){this[A]="";}else{this.removeAttribute(B);}return this;},getProperties:function(){return Element.getMany(this,"getProperty",arguments);},setProperty:function(C,B){var A=Element.Properties[C];
if(A){this[A]=B;}else{this.setAttribute(C,B);}return this;},setProperties:function(A){return Element.setMany(this,"setProperty",A);},setHTML:function(){this.innerHTML=$A(arguments).join("");
return this;},setText:function(B){var A=this.getTag();if(["style","script"].contains(A)){if(window.ie){if(A=="style"){this.styleSheet.cssText=B;}else{if(A=="script"){this.setProperty("text",B);
}}return this;}else{this.removeChild(this.firstChild);return this.appendText(B);}}this[$defined(this.innerText)?"innerText":"textContent"]=B;return this;
},getText:function(){var A=this.getTag();if(["style","script"].contains(A)){if(window.ie){if(A=="style"){return this.styleSheet.cssText;}else{if(A=="script"){return this.getProperty("text");
}}}else{return this.innerHTML;}}return($pick(this.innerText,this.textContent));},getTag:function(){return this.tagName.toLowerCase();},empty:function(){Garbage.trash(this.getElementsByTagName("*"));
return this.setHTML("");}});Element.fixStyle=function(E,A,D){if($chk(parseInt(A))){return A;}if(["height","width"].contains(E)){var B=(E=="width")?["left","right"]:["top","bottom"];
var C=0;B.each(function(F){C+=D.getStyle("border-"+F+"-width").toInt()+D.getStyle("padding-"+F).toInt();});return D["offset"+E.capitalize()]-C+"px";}else{if(E.test(/border(.+)Width|margin|padding/)){return"0px";
}}return A;};Element.Styles={border:[],padding:[],margin:[]};["Top","Right","Bottom","Left"].each(function(B){for(var A in Element.Styles){Element.Styles[A].push(A+B);
}});Element.borderShort=["borderWidth","borderStyle","borderColor"];Element.getMany=function(B,D,C){var A={};$each(C,function(E){A[E]=B[D](E);});return A;
};Element.setMany=function(B,D,C){for(var A in C){B[D](A,C[A]);}return B;};Element.Properties=new Abstract({"class":"className","for":"htmlFor",colspan:"colSpan",rowspan:"rowSpan",accesskey:"accessKey",tabindex:"tabIndex",maxlength:"maxLength",readonly:"readOnly",frameborder:"frameBorder",value:"value",disabled:"disabled",checked:"checked",multiple:"multiple",selected:"selected"});
Element.PropertiesIFlag={href:2,src:2};Element.Methods={Listeners:{addListener:function(B,A){if(this.addEventListener){this.addEventListener(B,A,false);
}else{this.attachEvent("on"+B,A);}return this;},removeListener:function(B,A){if(this.removeEventListener){this.removeEventListener(B,A,false);}else{this.detachEvent("on"+B,A);
}return this;}}};window.extend(Element.Methods.Listeners);document.extend(Element.Methods.Listeners);Element.extend(Element.Methods.Listeners);var Garbage={elements:[],collect:function(A){if(!A.$tmp){Garbage.elements.push(A);
A.$tmp={opacity:1};}return A;},trash:function(D){for(var B=0,A=D.length,C;B<A;B++){if(!(C=D[B])||!C.$tmp){continue;}if(C.$events){C.fireEvent("trash").removeEvents();
}for(var E in C.$tmp){C.$tmp[E]=null;}for(var F in Element.prototype){C[F]=null;}Garbage.elements[Garbage.elements.indexOf(C)]=null;C.htmlElement=C.$tmp=C=null;
}Garbage.elements.remove(null);},empty:function(){Garbage.collect(window);Garbage.collect(document);Garbage.trash(Garbage.elements);}};window.addListener("beforeunload",function(){window.addListener("unload",Garbage.empty);
if(window.ie){window.addListener("unload",CollectGarbage);}});var Event=new Class({initialize:function(C){if(C&&C.$extended){return C;}this.$extended=true;
C=C||window.event;this.event=C;this.type=C.type;this.target=C.target||C.srcElement;if(this.target.nodeType==3){this.target=this.target.parentNode;}this.shift=C.shiftKey;
this.control=C.ctrlKey;this.alt=C.altKey;this.meta=C.metaKey;if(["DOMMouseScroll","mousewheel"].contains(this.type)){this.wheel=(C.wheelDelta)?C.wheelDelta/120:-(C.detail||0)/3;
}else{if(this.type.contains("key")){this.code=C.which||C.keyCode;for(var B in Event.keys){if(Event.keys[B]==this.code){this.key=B;break;}}if(this.type=="keydown"){var A=this.code-111;
if(A>0&&A<13){this.key="f"+A;}}this.key=this.key||String.fromCharCode(this.code).toLowerCase();}else{if(this.type.test(/(click|mouse|menu)/)){this.page={x:C.pageX||C.clientX+document.documentElement.scrollLeft,y:C.pageY||C.clientY+document.documentElement.scrollTop};
this.client={x:C.pageX?C.pageX-window.pageXOffset:C.clientX,y:C.pageY?C.pageY-window.pageYOffset:C.clientY};this.rightClick=(C.which==3)||(C.button==2);
switch(this.type){case"mouseover":this.relatedTarget=C.relatedTarget||C.fromElement;break;case"mouseout":this.relatedTarget=C.relatedTarget||C.toElement;
}this.fixRelatedTarget();}}}return this;},stop:function(){return this.stopPropagation().preventDefault();},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();
}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();}else{this.event.returnValue=false;
}return this;}});Event.fix={relatedTarget:function(){if(this.relatedTarget&&this.relatedTarget.nodeType==3){this.relatedTarget=this.relatedTarget.parentNode;
}},relatedTargetGecko:function(){try{Event.fix.relatedTarget.call(this);}catch(A){this.relatedTarget=this.target;}}};Event.prototype.fixRelatedTarget=(window.gecko)?Event.fix.relatedTargetGecko:Event.fix.relatedTarget;
Event.keys=new Abstract({enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46});Element.Methods.Events={addEvent:function(C,B){this.$events=this.$events||{};
this.$events[C]=this.$events[C]||{keys:[],values:[]};if(this.$events[C].keys.contains(B)){return this;}this.$events[C].keys.push(B);var A=C;var D=Element.Events[C];
if(D){if(D.add){D.add.call(this,B);}if(D.map){B=D.map;}if(D.type){A=D.type;}}if(!this.addEventListener){B=B.create({bind:this,event:true});}this.$events[C].values.push(B);
return(Element.NativeEvents.contains(A))?this.addListener(A,B):this;},removeEvent:function(C,B){if(!this.$events||!this.$events[C]){return this;}var F=this.$events[C].keys.indexOf(B);
if(F==-1){return this;}var A=this.$events[C].keys.splice(F,1)[0];var E=this.$events[C].values.splice(F,1)[0];var D=Element.Events[C];if(D){if(D.remove){D.remove.call(this,B);
}if(D.type){C=D.type;}}return(Element.NativeEvents.contains(C))?this.removeListener(C,E):this;},addEvents:function(A){return Element.setMany(this,"addEvent",A);
},removeEvents:function(A){if(!this.$events){return this;}if(!A){for(var B in this.$events){this.removeEvents(B);}this.$events=null;}else{if(this.$events[A]){this.$events[A].keys.each(function(C){this.removeEvent(A,C);
},this);this.$events[A]=null;}}return this;},fireEvent:function(C,B,A){if(this.$events&&this.$events[C]){this.$events[C].keys.each(function(D){D.create({bind:this,delay:A,"arguments":B})();
},this);}return this;},cloneEvents:function(C,A){if(!C.$events){return this;}if(!A){for(var B in C.$events){this.cloneEvents(C,B);}}else{if(C.$events[A]){C.$events[A].keys.each(function(D){this.addEvent(A,D);
},this);}}return this;}};window.extend(Element.Methods.Events);document.extend(Element.Methods.Events);Element.extend(Element.Methods.Events);Element.Events=new Abstract({mouseenter:{type:"mouseover",map:function(A){A=new Event(A);
if(A.relatedTarget!=this&&!this.hasChild(A.relatedTarget)){this.fireEvent("mouseenter",A);}}},mouseleave:{type:"mouseout",map:function(A){A=new Event(A);
if(A.relatedTarget!=this&&!this.hasChild(A.relatedTarget)){this.fireEvent("mouseleave",A);}}},mousewheel:{type:(window.gecko)?"DOMMouseScroll":"mousewheel"}});
Element.NativeEvents=["click","dblclick","mouseup","mousedown","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","keydown","keypress","keyup","load","unload","beforeunload","resize","move","focus","blur","change","submit","reset","select","error","abort","contextmenu","scroll"];
Function.extend({bindWithEvent:function(B,A){return this.create({bind:B,"arguments":A,event:Event});}});Elements.extend({filterByTag:function(A){return new Elements(this.filter(function(B){return(Element.getTag(B)==A);
}));},filterByClass:function(A,C){var B=this.filter(function(D){return(D.className&&D.className.contains(A," "));});return(C)?B:new Elements(B);},filterById:function(C,B){var A=this.filter(function(D){return(D.id==C);
});return(B)?A:new Elements(A);},filterByAttribute:function(B,A,D,E){var C=this.filter(function(F){var G=Element.getProperty(F,B);if(!G){return false;}if(!A){return true;
}switch(A){case"=":return(G==D);case"*=":return(G.contains(D));case"^=":return(G.substr(0,D.length)==D);case"$=":return(G.substr(G.length-D.length)==D);
case"!=":return(G!=D);case"~=":return G.contains(D," ");}return false;});return(E)?C:new Elements(C);}});function $E(A,B){return($(B)||document).getElement(A);
}function $ES(A,B){return($(B)||document).getElementsBySelector(A);}$$.shared={regexp:/^(\w*|\*)(?:#([\w-]+)|\.([\w-]+))?(?:\[(\w+)(?:([!*^$]?=)["']?([^"'\]]*)["']?)?])?$/,xpath:{getParam:function(B,D,E,C){var A=[D.namespaceURI?"xhtml:":"",E[1]];
if(E[2]){A.push('[@id="',E[2],'"]');}if(E[3]){A.push('[contains(concat(" ", @class, " "), " ',E[3],' ")]');}if(E[4]){if(E[5]&&E[6]){switch(E[5]){case"*=":A.push("[contains(@",E[4],', "',E[6],'")]');
break;case"^=":A.push("[starts-with(@",E[4],', "',E[6],'")]');break;case"$=":A.push("[substring(@",E[4],", string-length(@",E[4],") - ",E[6].length,' + 1) = "',E[6],'"]');
break;case"=":A.push("[@",E[4],'="',E[6],'"]');break;case"!=":A.push("[@",E[4],'!="',E[6],'"]');}}else{A.push("[@",E[4],"]");}}B.push(A.join(""));return B;
},getItems:function(B,E,G){var F=[];var A=document.evaluate(".//"+B.join("//"),E,$$.shared.resolver,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);for(var D=0,C=A.snapshotLength;
D<C;D++){F.push(A.snapshotItem(D));}return(G)?F:new Elements(F.map($));}},normal:{getParam:function(A,C,E,B){if(B==0){if(E[2]){var D=C.getElementById(E[2]);
if(!D||((E[1]!="*")&&(Element.getTag(D)!=E[1]))){return false;}A=[D];}else{A=$A(C.getElementsByTagName(E[1]));}}else{A=$$.shared.getElementsByTagName(A,E[1]);
if(E[2]){A=Elements.filterById(A,E[2],true);}}if(E[3]){A=Elements.filterByClass(A,E[3],true);}if(E[4]){A=Elements.filterByAttribute(A,E[4],E[5],E[6],true);
}return A;},getItems:function(A,B,C){return(C)?A:$$.unique(A);}},resolver:function(A){return(A=="xhtml")?"http://www.w3.org/1999/xhtml":false;},getElementsByTagName:function(D,C){var E=[];
for(var B=0,A=D.length;B<A;B++){E.extend(D[B].getElementsByTagName(C));}return E;}};$$.shared.method=(window.xpath)?"xpath":"normal";Element.Methods.Dom={getElements:function(A,H){var C=[];
A=A.trim().split(" ");for(var E=0,D=A.length;E<D;E++){var F=A[E];var G=F.match($$.shared.regexp);if(!G){break;}G[1]=G[1]||"*";var B=$$.shared[$$.shared.method].getParam(C,this,G,E);
if(!B){break;}C=B;}return $$.shared[$$.shared.method].getItems(C,this,H);},getElement:function(A){return $(this.getElements(A,true)[0]||false);},getElementsBySelector:function(A,E){var D=[];
A=A.split(",");for(var C=0,B=A.length;C<B;C++){D=D.concat(this.getElements(A[C],true));}return(E)?D:$$.unique(D);}};Element.extend({getElementById:function(C){var B=document.getElementById(C);
if(!B){return false;}for(var A=B.parentNode;A!=this;A=A.parentNode){if(!A){return false;}}return B;},getElementsByClassName:function(A){return this.getElements("."+A);
}});document.extend(Element.Methods.Dom);Element.extend(Element.Methods.Dom);Element.extend({getValue:function(){switch(this.getTag()){case"select":var A=[];
$each(this.options,function(B){if(B.selected){A.push($pick(B.value,B.text));}});return(this.multiple)?A:A[0];case"input":if(!(this.checked&&["checkbox","radio"].contains(this.type))&&!["hidden","text","password"].contains(this.type)){break;
}case"textarea":return this.value;}return false;},getFormElements:function(){return $$(this.getElementsByTagName("input"),this.getElementsByTagName("select"),this.getElementsByTagName("textarea"));
},toQueryString:function(){var A=[];this.getFormElements().each(function(D){var C=D.name;var E=D.getValue();if(E===false||!C||D.disabled){return ;}var B=function(F){A.push(C+"="+encodeURIComponent(F));
};if($type(E)=="array"){E.each(B);}else{B(E);}});return A.join("&");}});Element.extend({scrollTo:function(A,B){this.scrollLeft=A;this.scrollTop=B;},getSize:function(){return{scroll:{x:this.scrollLeft,y:this.scrollTop},size:{x:this.offsetWidth,y:this.offsetHeight},scrollSize:{x:this.scrollWidth,y:this.scrollHeight}};
},getPosition:function(A){A=A||[];var B=this,D=0,C=0;do{D+=B.offsetLeft||0;C+=B.offsetTop||0;B=B.offsetParent;}while(B);A.each(function(E){D-=E.scrollLeft||0;
C-=E.scrollTop||0;});return{x:D,y:C};},getTop:function(A){return this.getPosition(A).y;},getLeft:function(A){return this.getPosition(A).x;},getCoordinates:function(B){var A=this.getPosition(B);
var C={width:this.offsetWidth,height:this.offsetHeight,left:A.x,top:A.y};C.right=C.left+C.width;C.bottom=C.top+C.height;return C;}});Element.Events.domready={add:function(B){if(window.loaded){B.call(this);
return ;}var A=function(){if(window.loaded){return ;}window.loaded=true;window.timer=$clear(window.timer);this.fireEvent("domready");}.bind(this);if(document.readyState&&window.webkit){window.timer=function(){if(["loaded","complete"].contains(document.readyState)){A();
}}.periodical(50);}else{if(document.readyState&&window.ie){if(!$("ie_ready")){var C=(window.location.protocol=="https:")?"://0":"javascript:void(0)";document.write('<script id="ie_ready" defer src="'+C+'"><\/script>');
$("ie_ready").onreadystatechange=function(){if(this.readyState=="complete"){A();}};}}else{window.addListener("load",A);document.addListener("DOMContentLoaded",A);
}}}};window.onDomReady=function(A){return this.addEvent("domready",A);};window.extend({getWidth:function(){if(this.webkit419){return this.innerWidth;}if(this.opera){return document.body.clientWidth;
}return document.documentElement.clientWidth;},getHeight:function(){if(this.webkit419){return this.innerHeight;}if(this.opera){return document.body.clientHeight;
}return document.documentElement.clientHeight;},getScrollWidth:function(){if(this.ie){return Math.max(document.documentElement.offsetWidth,document.documentElement.scrollWidth);
}if(this.webkit){return document.body.scrollWidth;}return document.documentElement.scrollWidth;},getScrollHeight:function(){if(this.ie){return Math.max(document.documentElement.offsetHeight,document.documentElement.scrollHeight);
}if(this.webkit){return document.body.scrollHeight;}return document.documentElement.scrollHeight;},getScrollLeft:function(){return this.pageXOffset||document.documentElement.scrollLeft;
},getScrollTop:function(){return this.pageYOffset||document.documentElement.scrollTop;},getSize:function(){return{size:{x:this.getWidth(),y:this.getHeight()},scrollSize:{x:this.getScrollWidth(),y:this.getScrollHeight()},scroll:{x:this.getScrollLeft(),y:this.getScrollTop()}};
},getPosition:function(){return{x:0,y:0};}});var Fx={};Fx.Base=new Class({options:{onStart:Class.empty,onComplete:Class.empty,onCancel:Class.empty,transition:function(A){return -(Math.cos(Math.PI*A)-1)/2;
},duration:500,unit:"px",wait:true,fps:50},initialize:function(A){this.element=this.element||null;this.setOptions(A);if(this.options.initialize){this.options.initialize.call(this);
}},step:function(){var A=$time();if(A<this.time+this.options.duration){this.delta=this.options.transition((A-this.time)/this.options.duration);this.setNow();
this.increase();}else{this.stop(true);this.set(this.to);this.fireEvent("onComplete",this.element,10);this.callChain();}},set:function(A){this.now=A;this.increase();
return this;},setNow:function(){this.now=this.compute(this.from,this.to);},compute:function(B,A){return(A-B)*this.delta+B;},start:function(B,A){if(!this.options.wait){this.stop();
}else{if(this.timer){return this;}}this.from=B;this.to=A;this.change=this.to-this.from;this.time=$time();this.timer=this.step.periodical(Math.round(1000/this.options.fps),this);
this.fireEvent("onStart",this.element);return this;},stop:function(A){if(!this.timer){return this;}this.timer=$clear(this.timer);if(!A){this.fireEvent("onCancel",this.element);
}return this;},custom:function(B,A){return this.start(B,A);},clearTimer:function(A){return this.stop(A);}});Fx.Base.implement(new Chain,new Events,new Options);
Fx.CSS={select:function(B,C){if(B.test(/color/i)){return this.Color;}var A=$type(C);if((A=="array")||(A=="string"&&C.contains(" "))){return this.Multi;
}return this.Single;},parse:function(C,D,A){if(!A.push){A=[A];}var F=A[0],E=A[1];if(!$chk(E)){E=F;F=C.getStyle(D);}var B=this.select(D,E);return{from:B.parse(F),to:B.parse(E),css:B};
}};Fx.CSS.Single={parse:function(A){return parseFloat(A);},getNow:function(C,B,A){return A.compute(C,B);},getValue:function(C,A,B){if(A=="px"&&B!="opacity"){C=Math.round(C);
}return C+A;}};Fx.CSS.Multi={parse:function(A){return A.push?A:A.split(" ").map(function(B){return parseFloat(B);});},getNow:function(E,D,C){var A=[];for(var B=0;
B<E.length;B++){A[B]=C.compute(E[B],D[B]);}return A;},getValue:function(C,A,B){if(A=="px"&&B!="opacity"){C=C.map(Math.round);}return C.join(A+" ")+A;}};
Fx.CSS.Color={parse:function(A){return A.push?A:A.hexToRgb(true);},getNow:function(E,D,C){var A=[];for(var B=0;B<E.length;B++){A[B]=Math.round(C.compute(E[B],D[B]));
}return A;},getValue:function(A){return"rgb("+A.join(",")+")";}};Fx.Style=Fx.Base.extend({initialize:function(B,C,A){this.element=$(B);this.property=C;
this.parent(A);},hide:function(){return this.set(0);},setNow:function(){this.now=this.css.getNow(this.from,this.to,this);},set:function(A){this.css=Fx.CSS.select(this.property,A);
return this.parent(this.css.parse(A));},start:function(C,B){if(this.timer&&this.options.wait){return this;}var A=Fx.CSS.parse(this.element,this.property,[C,B]);
this.css=A.css;return this.parent(A.from,A.to);},increase:function(){this.element.setStyle(this.property,this.css.getValue(this.now,this.options.unit,this.property));
}});Element.extend({effect:function(B,A){return new Fx.Style(this,B,A);}});Fx.Elements=Fx.Base.extend({initialize:function(B,A){this.elements=$$(B);this.parent(A);
},setNow:function(){for(var C in this.from){var F=this.from[C],E=this.to[C],B=this.css[C],A=this.now[C]={};for(var D in F){A[D]=B[D].getNow(F[D],E[D],this);
}}},set:function(G){var B={};this.css={};for(var D in G){var F=G[D],C=this.css[D]={},A=B[D]={};for(var E in F){C[E]=Fx.CSS.select(E,F[E]);A[E]=C[E].parse(F[E]);
}}return this.parent(B);},start:function(D){if(this.timer&&this.options.wait){return this;}this.now={};this.css={};var I={},J={};for(var E in D){var G=D[E],A=I[E]={},H=J[E]={},C=this.css[E]={};
for(var B in G){var F=Fx.CSS.parse(this.elements[E],B,G[B]);A[B]=F.from;H[B]=F.to;C[B]=F.css;}}return this.parent(I,J);},increase:function(){for(var C in this.now){var A=this.now[C],B=this.css[C];
for(var D in A){this.elements[C].setStyle(D,B[D].getValue(A[D],this.options.unit,D));}}}});Fx.Slide=Fx.Base.extend({options:{mode:"vertical"},initialize:function(B,A){this.element=$(B);
this.wrapper=new Element("div",{styles:$extend(this.element.getStyles("margin"),{overflow:"hidden"})}).injectAfter(this.element).adopt(this.element);this.element.setStyle("margin",0);
this.setOptions(A);this.now=[];this.parent(this.options);this.open=true;this.addEvent("onComplete",function(){this.open=(this.now[0]===0);});if(window.webkit419){this.addEvent("onComplete",function(){if(this.open){this.element.remove().inject(this.wrapper);
}});}},setNow:function(){for(var A=0;A<2;A++){this.now[A]=this.compute(this.from[A],this.to[A]);}},vertical:function(){this.margin="margin-top";this.layout="height";
this.offset=this.element.offsetHeight;},horizontal:function(){this.margin="margin-left";this.layout="width";this.offset=this.element.offsetWidth;},slideIn:function(A){this[A||this.options.mode]();
return this.start([this.element.getStyle(this.margin).toInt(),this.wrapper.getStyle(this.layout).toInt()],[0,this.offset]);},slideOut:function(A){this[A||this.options.mode]();
return this.start([this.element.getStyle(this.margin).toInt(),this.wrapper.getStyle(this.layout).toInt()],[-this.offset,0]);},hide:function(A){this[A||this.options.mode]();
this.open=false;return this.set([-this.offset,0]);},show:function(A){this[A||this.options.mode]();this.open=true;return this.set([0,this.offset]);},toggle:function(A){if(this.wrapper.offsetHeight==0||this.wrapper.offsetWidth==0){return this.slideIn(A);
}return this.slideOut(A);},increase:function(){this.element.setStyle(this.margin,this.now[0]+this.options.unit);this.wrapper.setStyle(this.layout,this.now[1]+this.options.unit);
}});Fx.Transition=function(B,A){A=A||[];if($type(A)!="array"){A=[A];}return $extend(B,{easeIn:function(C){return B(C,A);},easeOut:function(C){return 1-B(1-C,A);
},easeInOut:function(C){return(C<=0.5)?B(2*C,A)/2:(2-B(2*(1-C),A))/2;}});};Fx.Transitions=new Abstract({linear:function(A){return A;}});Fx.Transitions.extend=function(A){for(var B in A){Fx.Transitions[B]=new Fx.Transition(A[B]);
Fx.Transitions.compat(B);}};Fx.Transitions.compat=function(A){["In","Out","InOut"].each(function(B){Fx.Transitions[A.toLowerCase()+B]=Fx.Transitions[A]["ease"+B];
});};Fx.Transitions.extend({Pow:function(B,A){return Math.pow(B,A[0]||6);},Expo:function(A){return Math.pow(2,8*(A-1));},Circ:function(A){return 1-Math.sin(Math.acos(A));
},Sine:function(A){return 1-Math.sin((1-A)*Math.PI/2);},Back:function(B,A){A=A[0]||1.618;return Math.pow(B,2)*((A+1)*B-A);},Bounce:function(D){var C;for(var B=0,A=1;
1;B+=A,A/=2){if(D>=(7-4*B)/11){C=-Math.pow((11-6*B-11*D)/4,2)+A*A;break;}}return C;},Elastic:function(B,A){return Math.pow(2,10*--B)*Math.cos(20*B*Math.PI*(A[0]||1)/3);
}});["Quad","Cubic","Quart","Quint"].each(function(B,A){Fx.Transitions[B]=new Fx.Transition(function(C){return Math.pow(C,[A+2]);});Fx.Transitions.compat(B);
});var Drag={};Drag.Base=new Class({options:{handle:false,unit:"px",onStart:Class.empty,onBeforeStart:Class.empty,onComplete:Class.empty,onSnap:Class.empty,onDrag:Class.empty,limit:false,modifiers:{x:"left",y:"top"},grid:false,snap:6},initialize:function(B,A){this.setOptions(A);
this.element=$(B);this.handle=$(this.options.handle)||this.element;this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.bound={start:this.start.bindWithEvent(this),check:this.check.bindWithEvent(this),drag:this.drag.bindWithEvent(this),stop:this.stop.bind(this)};
this.attach();if(this.options.initialize){this.options.initialize.call(this);}},attach:function(){this.handle.addEvent("mousedown",this.bound.start);return this;
},detach:function(){this.handle.removeEvent("mousedown",this.bound.start);return this;},start:function(C){this.fireEvent("onBeforeStart",this.element);
this.mouse.start=C.page;var A=this.options.limit;this.limit={x:[],y:[]};for(var D in this.options.modifiers){if(!this.options.modifiers[D]){continue;}this.value.now[D]=this.element.getStyle(this.options.modifiers[D]).toInt();
this.mouse.pos[D]=C.page[D]-this.value.now[D];if(A&&A[D]){for(var B=0;B<2;B++){if($chk(A[D][B])){this.limit[D][B]=($type(A[D][B])=="function")?A[D][B]():A[D][B];
}}}}if($type(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid};}document.addListener("mousemove",this.bound.check);
document.addListener("mouseup",this.bound.stop);this.fireEvent("onStart",this.element);C.stop();},check:function(A){var B=Math.round(Math.sqrt(Math.pow(A.page.x-this.mouse.start.x,2)+Math.pow(A.page.y-this.mouse.start.y,2)));
if(B>this.options.snap){document.removeListener("mousemove",this.bound.check);document.addListener("mousemove",this.bound.drag);this.drag(A);this.fireEvent("onSnap",this.element);
}A.stop();},drag:function(A){this.out=false;this.mouse.now=A.page;for(var B in this.options.modifiers){if(!this.options.modifiers[B]){continue;}this.value.now[B]=this.mouse.now[B]-this.mouse.pos[B];
if(this.limit[B]){if($chk(this.limit[B][1])&&(this.value.now[B]>this.limit[B][1])){this.value.now[B]=this.limit[B][1];this.out=true;}else{if($chk(this.limit[B][0])&&(this.value.now[B]<this.limit[B][0])){this.value.now[B]=this.limit[B][0];
this.out=true;}}}if(this.options.grid[B]){this.value.now[B]-=(this.value.now[B]%this.options.grid[B]);}this.element.setStyle(this.options.modifiers[B],this.value.now[B]+this.options.unit);
}this.fireEvent("onDrag",this.element);A.stop();},stop:function(){document.removeListener("mousemove",this.bound.check);document.removeListener("mousemove",this.bound.drag);
document.removeListener("mouseup",this.bound.stop);this.fireEvent("onComplete",this.element);}});Drag.Base.implement(new Events,new Options);Element.extend({makeResizable:function(A){return new Drag.Base(this,$merge({modifiers:{x:"width",y:"height"}},A));
}});Drag.Move=Drag.Base.extend({options:{droppables:[],container:false,overflown:[]},initialize:function(B,A){this.setOptions(A);this.element=$(B);this.droppables=$$(this.options.droppables);
this.container=$(this.options.container);this.position={element:this.element.getStyle("position"),container:false};if(this.container){this.position.container=this.container.getStyle("position");
}if(!["relative","absolute","fixed"].contains(this.position.element)){this.position.element="absolute";}var D=this.element.getStyle("top").toInt();var C=this.element.getStyle("left").toInt();
if(this.position.element=="absolute"&&!["relative","absolute","fixed"].contains(this.position.container)){D=$chk(D)?D:this.element.getTop(this.options.overflown);
C=$chk(C)?C:this.element.getLeft(this.options.overflown);}else{D=$chk(D)?D:0;C=$chk(C)?C:0;}this.element.setStyles({top:D,left:C,position:this.position.element});
this.parent(this.element);},start:function(C){this.overed=null;if(this.container){var A=this.container.getCoordinates();var B=this.element.getCoordinates();
if(this.position.element=="absolute"&&!["relative","absolute","fixed"].contains(this.position.container)){this.options.limit={x:[A.left,A.right-B.width],y:[A.top,A.bottom-B.height]};
}else{this.options.limit={y:[0,A.height-B.height],x:[0,A.width-B.width]};}}this.parent(C);},drag:function(A){this.parent(A);var B=this.out?false:this.droppables.filter(this.checkAgainst,this).getLast();
if(this.overed!=B){if(this.overed){this.overed.fireEvent("leave",[this.element,this]);}this.overed=B?B.fireEvent("over",[this.element,this]):null;}return this;
},checkAgainst:function(B){B=B.getCoordinates(this.options.overflown);var A=this.mouse.now;return(A.x>B.left&&A.x<B.right&&A.y<B.bottom&&A.y>B.top);},stop:function(){if(this.overed&&!this.out){this.overed.fireEvent("drop",[this.element,this]);
}else{this.element.fireEvent("emptydrop",this);}this.parent();return this;}});Element.extend({makeDraggable:function(A){return new Drag.Move(this,A);}});
var XHR=new Class({options:{method:"post",async:true,onRequest:Class.empty,onSuccess:Class.empty,onFailure:Class.empty,urlEncoded:true,encoding:"utf-8",autoCancel:false,headers:{}},setTransport:function(){this.transport=(window.XMLHttpRequest)?new XMLHttpRequest():(window.ie?new ActiveXObject("Microsoft.XMLHTTP"):false);
return this;},initialize:function(A){this.setTransport().setOptions(A);this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.headers={};if(this.options.urlEncoded&&this.options.method=="post"){var B=(this.options.encoding)?"; charset="+this.options.encoding:"";
this.setHeader("Content-type","application/x-www-form-urlencoded"+B);}if(this.options.initialize){this.options.initialize.call(this);}},onStateChange:function(){if(this.transport.readyState!=4||!this.running){return ;
}this.running=false;var A=0;try{A=this.transport.status;}catch(B){}if(this.options.isSuccess.call(this,A)){this.onSuccess();}else{this.onFailure();}this.transport.onreadystatechange=Class.empty;
},isSuccess:function(A){return((A>=200)&&(A<300));},onSuccess:function(){this.response={text:this.transport.responseText,xml:this.transport.responseXML};
this.fireEvent("onSuccess",[this.response.text,this.response.xml]);this.callChain();},onFailure:function(){this.fireEvent("onFailure",this.transport);},setHeader:function(A,B){this.headers[A]=B;
return this;},send:function(A,C){if(this.options.autoCancel){this.cancel();}else{if(this.running){return this;}}this.running=true;if(C&&this.options.method=="get"){A=A+(A.contains("?")?"&":"?")+C;
C=null;}this.transport.open(this.options.method.toUpperCase(),A,this.options.async);this.transport.onreadystatechange=this.onStateChange.bind(this);if((this.options.method=="post")&&this.transport.overrideMimeType){this.setHeader("Connection","close");
}$extend(this.headers,this.options.headers);for(var B in this.headers){try{this.transport.setRequestHeader(B,this.headers[B]);}catch(D){}}this.fireEvent("onRequest");
this.transport.send($pick(C,null));return this;},cancel:function(){if(!this.running){return this;}this.running=false;this.transport.abort();this.transport.onreadystatechange=Class.empty;
this.setTransport();this.fireEvent("onCancel");return this;}});XHR.implement(new Chain,new Events,new Options);var Ajax=XHR.extend({options:{data:null,update:null,onComplete:Class.empty,evalScripts:false,evalResponse:false},initialize:function(B,A){this.addEvent("onSuccess",this.onComplete);
this.setOptions(A);this.options.data=this.options.data||this.options.postBody;if(!["post","get"].contains(this.options.method)){this._method="_method="+this.options.method;
this.options.method="post";}this.parent();this.setHeader("X-Requested-With","XMLHttpRequest");this.setHeader("Accept","text/javascript, text/html, application/xml, text/xml, */*");
this.url=B;},onComplete:function(){if(this.options.update){$(this.options.update).empty().setHTML(this.response.text);}if(this.options.evalScripts||this.options.evalResponse){this.evalScripts();
}this.fireEvent("onComplete",[this.response.text,this.response.xml],20);},request:function(A){A=A||this.options.data;switch($type(A)){case"element":A=$(A).toQueryString();
break;case"object":A=Object.toQueryString(A);}if(this._method){A=(A)?[this._method,A].join("&"):this._method;}return this.send(this.url,A);},evalScripts:function(){var B,A;
if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){A=this.response.text;}else{A=[];var C=/<script[^>]*>([\s\S]*?)<\/script>/gi;
while((B=C.exec(this.response.text))){A.push(B[1]);}A=A.join("\n");}if(A){(window.execScript)?window.execScript(A):window.setTimeout(A,0);}},getHeader:function(A){try{return this.transport.getResponseHeader(A);
}catch(B){}return null;}});Object.toQueryString=function(B){var C=[];for(var A in B){C.push(encodeURIComponent(A)+"="+encodeURIComponent(B[A]));}return C.join("&");
};Element.extend({send:function(A){return new Ajax(this.getProperty("action"),$merge({data:this.toQueryString()},A,{method:"post"})).request();}});var Cookie=new Abstract({options:{domain:false,path:false,duration:false,secure:false},set:function(C,D,B){B=$merge(this.options,B);
D=encodeURIComponent(D);if(B.domain){D+="; domain="+B.domain;}if(B.path){D+="; path="+B.path;}if(B.duration){var A=new Date();A.setTime(A.getTime()+B.duration*24*60*60*1000);
D+="; expires="+A.toGMTString();}if(B.secure){D+="; secure";}document.cookie=C+"="+D;return $extend(B,{key:C,value:D});},get:function(A){var B=document.cookie.match("(?:^|;)\\s*"+A.escapeRegExp()+"=([^;]*)");
return B?decodeURIComponent(B[1]):false;},remove:function(B,A){if($type(B)=="object"){this.set(B.key,"",$merge(B,{duration:-1}));}else{this.set(B,"",$merge(A,{duration:-1}));
}}});var Json={toString:function(C){switch($type(C)){case"string":return'"'+C.replace(/(["\\])/g,"\\$1")+'"';case"array":return"["+C.map(Json.toString).join(",")+"]";
case"object":var A=[];for(var B in C){A.push(Json.toString(B)+":"+Json.toString(C[B]));}return"{"+A.join(",")+"}";case"number":if(isFinite(C)){break;}case false:return"null";
}return String(C);},evaluate:function(str,secure){return(($type(str)!="string")||(secure&&!str.test(/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/)))?null:eval("("+str+")");
}};Json.Remote=XHR.extend({initialize:function(B,A){this.url=B;this.addEvent("onSuccess",this.onComplete);this.parent(A);this.setHeader("X-Request","JSON");
},send:function(A){return this.parent(this.url,"json="+Json.toString(A));},onComplete:function(){this.fireEvent("onComplete",[Json.evaluate(this.response.text,this.options.secure)]);
}});var Asset=new Abstract({javascript:function(C,B){B=$merge({onload:Class.empty},B);var A=new Element("script",{src:C}).addEvents({load:B.onload,readystatechange:function(){if(this.readyState=="complete"){this.fireEvent("load");
}}});delete B.onload;return A.setProperties(B).inject(document.head);},css:function(B,A){return new Element("link",$merge({rel:"stylesheet",media:"screen",type:"text/css",href:B},A)).inject(document.head);
},image:function(C,B){B=$merge({onload:Class.empty,onabort:Class.empty,onerror:Class.empty},B);var D=new Image();D.src=C;var A=new Element("img",{src:C});
["load","abort","error"].each(function(E){var F=B["on"+E];delete B["on"+E];A.addEvent(E,function(){this.removeEvent(E,arguments.callee);F.call(this);});
});if(D.width&&D.height){A.fireEvent("load",A,1);}return A.setProperties(B);},images:function(D,C){C=$merge({onComplete:Class.empty,onProgress:Class.empty},C);
if(!D.push){D=[D];}var A=[];var B=0;D.each(function(F){var E=new Asset.image(F,{onload:function(){C.onProgress.call(this,B);B++;if(B==D.length){C.onComplete();
}}});A.push(E);});return new Elements(A);}});var Accordion=Fx.Elements.extend({options:{onActive:Class.empty,onBackground:Class.empty,display:0,show:false,height:true,width:false,opacity:true,fixedHeight:false,fixedWidth:false,wait:false,alwaysHide:false},initialize:function(){var C,E,F,B;
$each(arguments,function(I,H){switch($type(I)){case"object":C=I;break;case"element":B=$(I);break;default:var G=$$(I);if(!E){E=G;}else{F=G;}}});this.togglers=E||[];
this.elements=F||[];this.container=$(B);this.setOptions(C);this.previous=-1;if(this.options.alwaysHide){this.options.wait=true;}if($chk(this.options.show)){this.options.display=false;
this.previous=this.options.show;}if(this.options.start){this.options.display=false;this.options.show=false;}this.effects={};if(this.options.opacity){this.effects.opacity="fullOpacity";
}if(this.options.width){this.effects.width=this.options.fixedWidth?"fullWidth":"offsetWidth";}if(this.options.height){this.effects.height=this.options.fixedHeight?"fullHeight":"scrollHeight";
}for(var D=0,A=this.togglers.length;D<A;D++){this.addSection(this.togglers[D],this.elements[D]);}this.elements.each(function(H,G){if(this.options.show===G){this.fireEvent("onActive",[this.togglers[G],H]);
}else{for(var I in this.effects){H.setStyle(I,0);}}},this);this.parent(this.elements);if($chk(this.options.display)){this.display(this.options.display);
}},addSection:function(E,C,G){E=$(E);C=$(C);var F=this.togglers.contains(E);var B=this.togglers.length;this.togglers.include(E);this.elements.include(C);
if(B&&(!F||G)){G=$pick(G,B-1);E.injectBefore(this.togglers[G]);C.injectAfter(E);}else{if(this.container&&!F){E.inject(this.container);C.inject(this.container);
}}var A=this.togglers.indexOf(E);E.addEvent("click",this.display.bind(this,A));if(this.options.height){C.setStyles({"padding-top":0,"border-top":"none","padding-bottom":0,"border-bottom":"none"});
}if(this.options.width){C.setStyles({"padding-left":0,"border-left":"none","padding-right":0,"border-right":"none"});}C.fullOpacity=1;if(this.options.fixedWidth){C.fullWidth=this.options.fixedWidth;
}if(this.options.fixedHeight){C.fullHeight=this.options.fixedHeight;}C.setStyle("overflow","hidden");if(!F){for(var D in this.effects){C.setStyle(D,0);
}}return this;},display:function(A){A=($type(A)=="element")?this.elements.indexOf(A):A;if((this.timer&&this.options.wait)||(A===this.previous&&!this.options.alwaysHide)){return this;
}this.previous=A;var B={};this.elements.each(function(E,D){B[D]={};var C=(D!=A)||(this.options.alwaysHide&&(E.offsetHeight>0));this.fireEvent(C?"onBackground":"onActive",[this.togglers[D],E]);
for(var F in this.effects){B[D][F]=C?0:E[this.effects[F]];}},this);return this.start(B);},showThisHideOpen:function(A){return this.display(A);}});Fx.Accordion=Accordion;
/*
Script: Hash.js
	Contains the class Hash.

License:
	MIT-style license.
*/

/*
Class: Hash
	It wraps an object that it uses internally as a map. The user must use set(), get(), and remove() to add/change, retrieve and remove values, it must not access the internal object directly. null/undefined values are allowed.

Note:
	Each hash instance has the length property.

Arguments:
	obj - an object to convert into a Hash instance.

Example:
	(start code)
	var hash = new Hash({a: 'hi', b: 'world', c: 'howdy'});
	hash.remove('b'); // b is removed.
	hash.set('c', 'hello');
	hash.get('c'); // returns 'hello'
	hash.length // returns 2 (a and c)
	(end)
*/

var Hash = new Class({

	length: 0,

	initialize: function(object){
		this.obj = object || {};
		this.setLength();
	},

	/*
	Property: get
		Retrieves a value from the hash.

	Arguments:
		key - The key

	Returns:
		The value
	*/

	get: function(key){
		return (this.hasKey(key)) ? this.obj[key] : null;
	},

	/*
	Property: hasKey
		Check the presence of a specified key-value pair in the hash.

	Arguments:
		key - The key

	Returns:
		True if the Hash contains a value for the specified key, otherwise false
	*/

	hasKey: function(key){
		return (key in this.obj);
	},

	/*
	Property: set
		Adds a key-value pair to the hash or replaces a previous value associated with the key.

	Arguments:
		key - The key
		value - The value
	*/

	set: function(key, value){
		if (!this.hasKey(key)) this.length++;
		this.obj[key] = value;
		return this;
	},

	setLength: function(){
		this.length = 0;
		for (var p in this.obj) this.length++;
		return this;
	},

	/*
	Property: remove
		Removes a key-value pair from the hash.

	Arguments:
		key - The key
	*/

	remove: function(key){
		if (this.hasKey(key)){
			delete this.obj[key];
			this.length--;
		}
		return this;
	},

	/*
	Property: each
		Calls a function for each key-value pair. The first argument passed to the function will be the value, the second one will be the key, like $each.

	Arguments:
		fn - The function to call for each key-value pair
		bind - Optional, the object that will be referred to as "this" in the function
	*/

	each: function(fn, bind){
		$each(this.obj, fn, bind);
	},

	/*
	Property: extend
		Extends the current hash with an object containing key-value pairs. Values for duplicate keys will be replaced by the new ones.

	Arguments:
		obj - An object containing key-value pairs
	*/

	extend: function(obj){
		$extend(this.obj, obj);
		return this.setLength();
	},

	/*
	Property: merge
		Merges the current hash with multiple objects.
	*/

	merge: function(){
		this.obj = $merge.apply(null, [this.obj].extend(arguments));
		return this.setLength();
	},

	/*
	Property: empty
		Empties all hash values properties and values.
	*/

	empty: function(){
		this.obj = {};
		this.length = 0;
		return this;
	},

	/*
	Property: keys
		Returns an array containing all the keys, in the same order as the values returned by <Hash.values>.

	Returns:
		An array containing all the keys of the hash
	*/

	keys: function(){
		var keys = [];
		for (var property in this.obj) keys.push(property);
		return keys;
	},

	/*
	Property: values
		Returns an array containing all the values, in the same order as the keys returned by <Hash.keys>.

	Returns:
		An array containing all the values of the hash
	*/

	values: function(){
		var values = [];
		for (var property in this.obj) values.push(this.obj[property]);
		return values;
	}

});

/* Section: Utility Functions */

/*
Function: $H
	Shortcut to create a Hash from an Object.
*/

function $H(obj){
	return new Hash(obj);
};/**
 * @class
 * @name Hash
 */
Hash.implement({
  /**
   * Converts this Hash to an array by calling a function once for each key-value pair.
   * @param {Function} fn The function to call for each key-value pair
   * @returns {Array} The resulting array.
   */
  collect: function(fn) {
    var result = [];
    this.each(function(v, k){
      result.push(fn(v, k));
    });
    return result;
  },
  
  /**
   * Modifies this Hash by calling a function once for each key-value pair and updating the value
   * to the return value of the function.
   * @example
   *   Uppercase all values:
   *   myHash.map(function(v, k) { return v.toUpperCase(); });
   * @param {Function} fn The function to call for each key-value pair
   * @returns {Hash} This Hash
   */
  map: function(fn) {
    this.keys().each(function(k) {
      this.set(k, fn(this.get(k), k));
    }, this);
    return this;
  }
});if (!$defined(IS_DEBUG)) var IS_DEBUG = false;
if (!$defined(VERSION_MAP)) var VERSION_MAP = {};
if (!$defined(CDNHOST)) var CDNHOST = '';

/**
 * Top level namespace.
 * @namespace
 */
var ta = {};

/**
 * Floating layer support.
 * @namespace
 */
ta.overlays = {};

/**
 * Google Maps extensions and related.
 * @namespace
 */
ta.maps = {};

/**
  Shared functionality
  @namespace
*/
ta.common = {};

ta.phac = {};

/**
  Servlet specific support functions.
  @namespace
*/
ta.servlet = {};

/**
 * Support classes.
 * @namespace
 */
ta.support = {};

/**
 * Utililty methods and classes.
 * @namespace
 */
ta.util = {};

/**
 * Common UI widgets.
 * @namespace
 */
ta.widgets = {};

/**
  Commerce related methods and classes.
  @namespace
*/
ta.commerce = {};


/*

  Event Delegation

*/

/**
 * Call the specified behavior handler function.
 * @example onclick="ta.call('ta.namespace.Class.function', event)"
 * @example ta.namespace.Class.function = function(evnt, elmt);
 * @param {String} funcName Name of the function to call
 * @param {Event} evnt The event
 * @param {Elmt} [elmt] 'this'
 * @returns {boolean} The return value of the function.
 */
ta.call = function(funcName, evnt, elmt) {
  if (!$defined(evnt)) {
    if (IS_DEBUG) alert("You must pass the event.\n\nUsage:\nta.call('ta.namespace.Class.function', event);");
    return false;
  }
  if (!/^ta\.[\w\d\.]+$/.test(funcName)) {
    if (IS_DEBUG) alert("Invalid function name. Must be of the form: ta.namespace.Class.function");
    return false;
  }

  var e     = new Event(evnt || window.event);
  if (!elmt) elmt = $(e.target);

  // try to find and run the function
  try {
    var fn = eval(funcName);
    if ($defined(fn) && $type(fn) == "function") return fn(e, elmt);
  }
  catch (_ex) {
    if (IS_DEBUG) alert("ERROR:\n"+_ex.message);
    // do nothing
  }
  
  // function not available, try to load dependency
  e.preventDefault(); // function deferred, prevent default event
  if (ta.load(funcName.split('.').slice(0,-1))) {
    ta.util.pending.waitForHandler(funcName.split('.').slice(-1), funcName, e, elmt);
  }
  return false;
}

/**
 * Dynamically loads the javascript necessary to include the given class.
 * @param {String} cl Name of class to load
 */
ta.load = function(cl) {
  if ($type(cl) == "string") cl = cl.split('.');
  var file = cl.join('-')+'.js';
  if (!VERSION_MAP[file]) file = cl.slice(0,-1).join('-')+'.js';
  if (!VERSION_MAP[file]) {
    return false;
  }
  return ta.loadFile(file);
}

/**
 * Dynamically loads a javascript file. File must be defined in the VERSION_MAP, usually by specifying
 * $onDemandScript in Velocity.
 * @example #set( $onDemandScript = ['ta-maps.js'] )
 * @param {String} file Name of file to load
 * @returns {Boolean} true if file can be loaded, false if not
 */
ta.loadFile = function(file) {
  if (!VERSION_MAP[file]) {
    if (IS_DEBUG) alert("File not found in version map: "+file);
    return false;
  }
  file = VERSION_MAP[file];
  if (IS_DEBUG) file += "?nocache=" + new Date().getTime();
  new Asset.javascript(file);
  return true;
}


/*

  On Load Queue

*/

/**
 * @private
 */
ta._loadQ = [];

/**
 * Add a function to be run when the page loads.
 * @param {function} fn function to run
 */
ta.queueForLoad = function(fn) {
  ta._loadQ.push(fn);
}

/**
 * Runs any functions in the on load queue.
 */
ta.onLoad = function() {
  for (var i = 0; i < ta._loadQ.length; i++) {
    ta._loadQ[i]();
  }
}
window.addEvent('load', ta.onLoad);


/*

  Variable Store

*/

/**
 * @private
 */
ta._store = new Hash();

/**
 * Stores a variable in the variable store.
 * @param {String} name variable name
 * @param {mixed} val value
 */
ta.store = function(name, val) {
  ta._store.set(name, val);
}

/**
 * Stores a variable in the variable store. If the variable already exists and is an array, appends
 * val to the end of the array. If it is not an array, it is converted to one with both values.
 * @param {String} name variable name
 * @param {mixed} val value
 */
ta.keep = function(name, val){
  var k = [];
  var x = ta.retrieve(name);
  if (x){
    if (x instanceof Array) k = x;
    else k.push(x);
  }
  if (val instanceof Array) k = k.merge(val);
  else k.push(val);
  ta.store(name, k);
}

/**
 * Check to see if a variable is defined.
 * @param {String} name variable name
 * @returns {boolean} true if variable exists
 * @retruns {boolean} false if variable is not present
 */
ta.has = function(name) {
  return ta._store.hasKey(name);
}

/**
 * Return the value of a variable.
 * @param {String} name variable name
 * @returns {mixed} the variable value
 */
ta.retrieve = function(name) {
  return ta._store.get(name);
}

/**
 * Return the value of a variable as an integer.
 * @param {String} name variable name
 * @param {integer} [dfltVal=0] return value if parsing fails
 * @returns {integer} the variable value as an int
 * @returns {mixed} dfltVal
 */
ta.asInt = function(name, dfltVal) {
  var i = parseInt(ta.retrieve(name));
  return isNaN(i) ? (dfltVal || 0) : i;
}

/**
 * Return the value of a variable as a float.
 * @param {String} name variable name
 * @param {float} [dfltVal=0] return value if parsing fails
 * @returns {float} the variable value as a float
 * @returns {mixed} dfltVal
 */
ta.asFloat = function(name, dfltVal) {
  var f = parseFloat(ta.retrieve(name));
  return isNaN(f) ? (dfltVal || 0) : f;
}

/**
 * Removes a variable from the variable store and returns it.
 * @param {String} name variable name
 * @returns {mixed} the variable
 */
ta.remove = function(name) {
  var tmp = ta._store.get(name);
  ta._store.remove(name);
  return tmp;
}


/** @namespace */
ta.util.cookie = {};

/**
  Standard call for setting the PID based on a click action.
  @example <a href="..." class="pid_123" onclick="ta.call('ta.util.cookie.setPID', event)">Click</a>
  @param {Event} evnt The event
  @param {Element} container Element that will contain the map
*/
ta.util.cookie.setPID = function(e, elmt){  
  if ($(elmt).getTag() != "a") elmt = elmt.getParent("a");
  if (pid = elmt.className.match(/pid(\d+)/)) {
    ta.util.cookie.setPIDCookie(pid[1]);
  }
}

ta.util.cookie.setPIDCookie = function(pid){
	var taDomain = ta.retrieve('util.cookie.cookieDomain');
	Cookie.set('NPID', pid, {domain: taDomain, time:5, path: "/"});
}/**
 * 
 * @author jnowell
 * @since  2009.06.26
 */
ta.util.ASDF = {
		      
	  asdf: function(str) {
		window.location = asdf(str);
	  },
		  
	  asdfPopup: function(str) {
		 ta.servlet.Popup.open(asdf(str));
	  },
	  
	  asdfPopupEmail: function(str) {
		  ta.servlet.Popup.open(asdf(str),'email',580,460,30,25);
	  },
	  
	  asdfPopupGuide: function(str) {
		  ta.servlet.Popup.open(asdf(str),'dest_guide',565,700,(screen.width-700)/2,(screen.height-600)/2);
	  },
	  
	  asdfPopupTerms: function(str) {
		  ta.servlet.Popup.open(asdf(str),'terms',300,300,30,25);
	  }
	  
}
    	
function getOffset(c){
    if (c >= 97 && c<=122) return c-61;
    if (c >= 65 && c<=90) return c-55;
    if (c >= 48 && c<=71) return c-48;
    return -1;
}

function asdf(sInput){

	  var s = "";
	  for (var i=0; i<sInput.length; i++){
	    var c = sInput.charAt(i);
	    var p=c;
	    if (OBF[c] && i+1<sInput.length){
    	    i++;
    	    p+=sInput.charAt(i); 
    	} else {
    	    c="";
    	}
    	//s+="("+p+")"; 
    	var o = getOffset(sInput.charCodeAt(i));
    	if ( o<0 || typeof OBF[c][o]=="String"){
    	    s+=p;
    	} else {
    	    s+=OBF[c][o];
    	}
    	//s += " ";
      } 
    		  
     return s;
}
/** @namespace */
ta.util.pending = {};

/**
 * Calls a function after a delay. Resets the timer if it is already active.
 * @param {String} id timer name
 * @param {Function} fn function to call when timer is up
 * @param {integer} limit time to wait in ms
 */
ta.util.pending.waitForMore = function(id, fn, limit) {
  if (ta.has('pending.'+id)) ta.util.pending.cancel(id);
  ta.store('pending.'+id, {timer: fn.delay(limit), 'fn': fn, t:'m', n: limit});
}

/**
 * Waits until a function is loaded, then calls it.
 * @param {String} id timer name
 * @param {String} fn function name
 */
ta.util.pending.waitForFn = function(id, fn) {
  // TODO assert that fn is a valid function name, i.e. only letters and periods.
  try {
    var tmp = eval(fn);
    if ($defined(tmp)) {
      if ($type(tmp) == 'function') {
        if (ta.has('pending.'+id)) {
          var data = ta.retrieve('pending.'+id);
          if (data.evnt) {
            tmp.attempt([data.evnt, data.elmt]);
          }
          else tmp.attempt();
        }
        else tmp.attempt();
      }
      else if (IS_DEBUG) {
        alert("fn is not a function: " + $type(tmp));
      }
    }
    else {
      ta.util.pending.waitForFnAgain(id, fn);
    }
  }
  catch (e) {
    ta.util.pending.waitForFnAgain(id, fn);
  }
}

/**
 * Time interval between checks.
 * @private
 */
ta.util.pending.WAIT_DELAY = 100;
/**
 * Maximum times to run check.
 * @private
 */
ta.util.pending.WAIT_TIMEOUT = 50;

/** @private */
ta.util.pending.waitForFnAgain = function(id, fn) {
  var data = ta.has('pending.'+id) ? ta.retrieve('pending.'+id) : {t:'f', 'fn':fn, count:1};
  if (data.count < ta.util.pending.WAIT_TIMEOUT) {
    data.count++;
    data.timer = ta.util.pending.waitForFn.delay(ta.util.pending.WAIT_DELAY, null, [id, fn]);
    ta.store('pending.'+id, data);
  }
  else if (IS_DEBUG)
    alert('timed out (' +
      (ta.util.pending.WAIT_TIMEOUT * ta.util.pending.WAIT_DELAY) +
      'ms) waiting for function: ' + fn
    );
}

/**
 * Attempts to load a file dynamically, then runs the callback function.
 * @param {String} id timer name
 * @param {String} file Name of file to load
 * @param {String} fn Function name
 */
ta.util.pending.waitForFile = function(id, file, fn) {
  if (ta.loadFile(file)) {
    ta.util.pending.waitForFn(id, fn);
  }
}

/**
 * Calls an event handler function after its loaded.
 * @param {String} id timer name
 * @param {String} fn Function name
 * @param {Event} evnt The Event
 * @param {Element} elmt The Element
 */
ta.util.pending.waitForHandler = function(id, fn, evnt, elmt) {
  ta.store('pending.'+id, {
    count: 0,
    t: 'f',
    'fn': fn,
    'evnt': evnt,
    'elmt': elmt
  });
  ta.util.pending.waitForFn(id, fn);
}

/**
 * Cancels a timer.
 * @param {String} id timer name
 */
ta.util.pending.cancel = function(id) {
  var p = ta.remove('pending.'+id);
  if (p && p.timer) $clear(p.timer);
}

/**
 * Prevents an action from occurring if it is locked.
 * @param {String} id lock name
 * @param {Function} fn function to run
 * @param {integer} [timer] automatically unlock after this many ms
 * @param {mixed} [fail=false] return value if action is locked
 * @returns {mixed} function result if not locked, fail parameter otherwise
 */
ta.util.pending.lock = function(id, fn, timer, fail) {
  if (ta.has('pending.'+id)) return $pick(fail, false);
  ta.store('pending.'+id, true);
  if ($defined(timer)) ta.util.pending.unlock.delay(timer, null, id);
  return fn();
}

/**
 * Unlocks an action.
 * @param {String} id lock name
 */
ta.util.pending.unlock = function(id) {
  ta.remove('pending.'+id);
}
/**
  Dynamic loading and dependancy support.
  @namespace
*/
ta.util.load = {};

/**
 * Load the Google Maps API. Uses the following settings if not passed in:
 *   * _maps.callback_ - Function to call when maps api is loaded.
 * @param {Function} [callback] Function to call when maps api is loaded.
 * @returns {boolean} true if maps api is already loaded
 * @returns {boolean} false if waiting for maps api to load
 * @author wasche
 * @since 2009.02.09
 */
ta.util.load.GMaps = function(callback) {
  callback = $pick(callback, ta.retrieve('maps.callback'));
  if ($defined(window['GUnload']) && callback) {
    callback(); // already loaded
    return true;
  }
  if (callback) ta.store('gmaps.callback', callback);
  new Asset.javascript('http://maps.google.com/maps?file=api&v=2.118&key=' + gKey + '&c&async=2&callback=ta.util.load.GMapsCallback');
  return false;
}

/**
 * @private
 * Sets up the GUnload method to run when the page is unloaded and runs the callback method, if defined.
 * @author wasche
 * @since 2009.02.09
 */
ta.util.load.GMapsCallback = function() {
  if (!GBrowserIsCompatible()) return; // Maps not supported
  window.addListener('beforeunload', function(){
    window.addListener('unload', GUnload);
  });
  if (ta.has('gmaps.callback')) {
    ta.util.pending.waitForFn('gmaps.callback', ta.remove('gmaps.callback'));
  }
}

ta.util.load.GoogleLoader = function(callback) {
  if (ta.has("googleloader.state")) {
    if (ta.retrieve("googleloader.state") == "loaded") {
      return callback();
    }
    else {
      return ta.util.load.GoogleLoader.delay(500, this, callback);
    }
  }
  else {
    ta.store("googleloader.state", "loading");
    ta.store("googleloader.callback", callback);
    new Asset.javascript('http://www.google.com/jsapi?key=' + gKey + "&callback=ta.util.load.GoogleLoaderCallback");
  }
}

ta.util.load.GoogleLoaderCallback = function() {
  ta.store("googleloader.state", "loaded");
  var callback = ta.remove("googleloader.callback");
  callback();
}

/**
 * Load the Google Local Search API.
 * @author hhwang
 * @since  8 Apr 2009
 */
ta.util.load.LocalSearch = function(callback) {
  ta.util.load.GoogleLoader(function() {
                              google.load("search", "1", {callback: function() {}});
                            });
};
/** @namespace */
ta.util.date = {};

/** The number of ms in a day  */
ta.util.date.MS_IN_DAY = 1000 * 60 * 60 * 24;

/** The number of days in a month */
ta.util.date.DAYS_IN_MONTH = [31,28,31,30,31,30,31,31,30,31,30,31];

/**
 * Check if a date falls in a date set.  A date set is an array containing Date objects
 * or a pair of Date objects (i.e. a range).  This function gets called a lot by the 
 * vacation rentals calendar and so needs to perform well.
 * @param {Date} date the date
 * @param {Array} dateSet the set of individual dates and/or ranges
 */
ta.util.date.inDateSet = function(date, dateSet)
{
    if ((date == null) || (dateSet == null)) return false;
    var length = dateSet.length;
    for (var i = 0; i < length; i++)
    {
      var dateSetItem = dateSet[i];
      if (dateSetItem.length) //  quick way to check if it is an array
      {
         var start = dateSetItem[0].getTime();
         var end = dateSetItem[1].getTime();
         
         //  if the date occurs in the range
         if ((start <= date.getTime()) && (end >= date.getTime()))
         {
             return true;
         }
      }
      
      //  else if 'dateSetItem' is a date and they match
      else if (date.getTime() == dateSetItem.getTime()) return true;
    }
    return false;
};

/**
 * Check if two ranges of dates intersect
 * @param {Array} dates1 an array containing 2 dates (i.e. the first date range)
 * @param {Array} dates2 an array containing 2 dates (i.e. the second date range)
 */
ta.util.date.rangesIntersect = function(dates1, dates2)
{
    //  grab the latest start and the earliest end and check if the remaining period of time is empty
    return Math.max(dates1[0].getTime(), dates2[0].getTime()) <= Math.min(dates1[1].getTime(), dates2[1].getTime()); 
};

/**
 * Count the number of days in a range (inclusive)
 * @param {Date} date1 the first date
 * @param {Date} date2 the second date
 * @return the number of days between two dates
 */
ta.util.date.getDaysInRange = function(date1, date2)
{
    return Math.round((date2.getTime() - date1.getTime()) / ta.util.date.MS_IN_DAY) + 1;
    
};

/**
 * Check that two dates are both not null and occur in the same month/year.
 * @param {Date} date1 first date
 * @param {Date} date2 second date
 */
ta.util.date.inSameMonth = function(date1, date2)
{
  return (date1 != null) &&
         (date2 != null) &&
         (date1.getMonth() == date2.getMonth()) &&
         (date1.getYear() == date2.getYear());
};
/** @namespace */
ta.util.currency = {};

/**
 * Format a number with a currency symbol
 * @param {Number} amount the amount to format
 * @param {String} the currency code
 * @return the formatted currency
 */
ta.util.currency.formatCurrency = function(/* Number */ amount,
                                           /* String */ currency)
{
  var selCurrency = currency ? currency : 'USD';
  var curOptions = currencySymbol[selCurrency];
  var tempNum = ta.util.currency.formatThousands(Math.round(amount));
  if(!curOptions[1])
  {
    return curOptions[0] + tempNum;
  }
  else
  {
    return tempNum + curOptions[0];
  }
};

/**
 * Format a number in thousands. Note this assumes num is an integer, no decimals
 * @param {Number} num
 * @return the number formatted in thousands
 */
ta.util.currency.formatThousands = function(/* Number */ num)
{
    if(typeof groupingSize == "undefined" || typeof groupingSeparator == "undefined" || groupingSize < 1)
    {
        return num;
    }
    var result = "";
    var tempString = "" + num;
    for(var i = 1; i <= tempString.length; i++)
    {
        result = tempString.charAt(tempString.length - i) + result;
        if(i % groupingSize == 0 && i < tempString.length)
        {
            result = groupingSeparator + result;
        }
    }
    return result;
};/**
 *
 * @namespace
 */
ta.util.Toggle = {}

/**
 * Toggles (between block and none) all sibling nodes.
 * @param {Event} evnt The Event
 * @param {Element} elmt The Element
 */
ta.util.Toggle.siblings = function(evnt, elmt) {
  evnt.preventDefault();
  var open = true;
  if (elmt.hasClass('closed')) open = false;
  elmt.toggleClass('closed');
  elmt.getParent().getChildren().each(function(n) {
    if (n != elmt) n.setStyle('display', open ? 'none' : 'block');
  });
};

ta.util.Toggle.parentClass = function(evnt, elmt) {
  evnt.preventDefault();
  var parent = elmt.getParent('.toggle');
  parent.toggleClass('off');
};

/**
 * 
 * @param {Element} input radio button clicked on
 * @param {String} formType form type to toggle on
 * @param {Event} evnt The Event
 * @returns {boolean} false
 */
ta.util.Toggle.toggleForm = function(input, formType, event) {
  var container = $(input).getParent('.formToggle');
  if (/sel_(\w+)/.test(container.className)) {
    var sel = RegExp.$1;
    container.removeClass('sel_'+sel);
    container.getElement('.form_'+sel).hide();
    var hdr = container.getElement('.header_'+sel);
    if (hdr) hdr.hide();
  }
  container.addClass('sel_'+formType);
  var hdr = container.getElement('.header_'+formType);
  if (hdr) hdr.show();
  container.getElement('.form_'+formType).show();
  return false;
};

/**
 * Toggle for a group of siblings elements where there is element for show, folloed by elements to show, followed by an element to hide.
 */
ta.util.Toggle.toggleLI = function(showElmt, hideElmt, show)
{
  var nextE = showElmt.getNext();

  // Loop siblings (or some reasonable max) until the hide element is reached.
  for(var i = 0; i<1000 && nextE && nextE.id != hideElmt.id; i++) {
    if (show) nextE.show(); else nextE.hide();
    nextE = nextE.getNext();
  }

  if (show) showElmt.hide(); else showElmt.show();
  if (show) hideElmt.show(); else hideElmt.hide();
};
/**
 * @class
 * Utility class to convert obfuscated URLs to real ones. This is to stop robots from parsing URLs
 * from the JavaScript.
 * 
 * @author wasche
 * @since  2009.02.17
 */
ta.util.URL = {
  /**
   * Reconstructs a URL from an obfuscated form. Supported forms are:
   *   * DOM:css selector -- selector must match a link, uses the href of the matched element
   *   * GMapsLC:Action,gXXX,ext -- uses GMapsLocationController, automatically adds from as current servlet
   *   * ABS:path -- makes an absolute url using the current hostname and the given path
   *   * REL:path -- maeks an absolute url using the current hostname, location path, and the given path
   *
   * @example DOM:#hotel_38519 .title a
   * @example GMapsLC:info,g60745,ext
   *
   * @param {String} str The URL to parse.
   * @returns {String} The reconstructed URL.
   */
  parse: function(str) {
  	if (/^(\w+):(.*)/.test(str)) {
      str = RegExp.$2;
      switch (RegExp.$1) {
        case 'DOM':
          elmt = document.getElement(str);
          str = (elmt && elmt.href)  ? elmt.href : '#';
          break;
        case 'GMapsLC':
          if (str.length == 0) break;
          tmp = str.split(',');
          ops = {from: pageServlet, Action: tmp.shift()};
          tmp.each(function(v){
            if (/^g(\d+)$/.test(v)) ops.g = RegExp.$1;
            else if ('ext' == v) ops.ext = 'y';
            else if (/^(.*)=(.*)$/.test(v)) ops[RegExp.$1] = RegExp.$2;
            else ops[v] = '';
          });
          str = '/GMapsLocationController?' + Object.toQueryString(ops);
          break;
        case 'ABS':
          var port = (window.location.port == "80" || window.location.port == "")? "" : (":" + window.location.port);
          str = window.location.protocol + '//' + window.location.hostname + port + '/' + str;
          break;
        case 'REL':
          var path = window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/'));
          if (/\/$/.test(window.location.pathname)) path = window.location.pathname;
          var port = window.location.port == "80" ? "" : (":" + window.location.port);
          str = window.location.protocol + '//' + window.location.hostname + port + path + '/' + str;
          break;
        default: 
          str = RegExp.$1 + ":" + str;
          break;
      }
    }
    return str;
  }  

}
ta.maps.floatmap = {

  deferFM: function(e) {
    new Asset.javascript(mapsJs);
    new Event(e).stop();
  },

  setupFloatMap: function() {
    var floatMap = $("FLOATMAP");
    fmThumb = floatMap.getElement(".js_floatMap");
    fmThumb.myDeferFn = function(e) { eval('ta.maps.floatmap.deferFM')(e); };
    fmThumb.addtl = new Array();
    fmThumb.addtl.push(floatMap.getElement('div.all a').addEvent('click', fmThumb.myDeferFn));
    var ln = $('LEFTNAV').getElement('a.k_maps') ||  $('LEFTNAV').getElement('span.k_maps');
    if (ln)
    {
      fmThumb.addtl.push(ln.addEvent('click', fmThumb.myDeferFn));
    }
    fmThumb.addEvent('click', fmThumb.myDeferFn);
  },
  
  setupCNFloatMap: function() {
    var cnFloatMapLink = $('CNFLOATMAP').getElement('a');
    var floatMapThumb  = $('FLOATMAP').getElement('.js_floatMap');
    floatMapThumb.addtl.push(cnFloatMapLink.addEvent('click', floatMapThumb.myDeferFn));    
  },
  
  termsPopup: function(event, elmt) {
    popup(elmt, ['terms', 300, 300, 30, 25]);
    return false;
  }

};ta.common.search = {

  langSelect: function(event, elmt) {
    if (elmt.form.elements['q'].value.length > 0)
    {
        elmt.form.submit();
    }
  },
  
  clearOnFocus: function(event, elmt) {
    if (elmt.value == elmt.defaultValue)
    {
      elmt.value = '';
      elmt.removeClass('focusClear');
    }
  }
  
};ta.common.flyout = {

  /**
   * Creates the Internation Sites overlay.
   * 
   * @param evnt click event
   * @param elmt the LI
   */
  intlPopup: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;

    if (!$defined(flagsUrl) || flagsUrl == ''){
      flagsUrl = document.location.pathname;
    }
    var uri = '/StaticVelocityPage?t=intSitesFly.vm&lt=evt&footerFlagFormat=' + footerFlagFormat + '&flagsURL=' + encodeURIComponent(flagsUrl);
    if (document.location.search != '') uri += encodeURIComponent(document.location.search);
    if (document.location.hash != '') uri += encodeURIComponent(document.location.hash);
    if (window['modelGeoId']) uri += '&geo=' + modelGeoId;
    if (window['flagsSettings']) {
        for(var k = 0; k < flagsSettings.length; k++) {
            uri += '&' + flagsSettings[k] + '=true';
        }
    }

    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: uri
    }, elmt);
    return false;
  },
  
  /**
   * "What's This?" Top Value overlay
   */
  whatIsTopValueFlyout:function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;
    //  grab the type class
    var typeClass = elmt.className.match(/\sb?(bv\S*)/);
    typeClass = typeClass.length > 0 ? typeClass[1] : null;
    if (typeClass)
    {
      new ta.overlays.RelativeOverlayBelow({
        showCloseButton: true,
        activate: ta.overlays.ACTIVATE_CLICK,
        content: document.getElement('.'+typeClass+'Info').innerHTML
      }, elmt);
      new Ajax('/ActionRecord').request();
    }
  },
  
  /**
   * Create the Vacation Rentals Owner Type overlay.
   */
  flipKeyOwnerTypeFlyout:function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;
    //  grab the type class
    var typeClass = elmt.className.match(/\sb?(vr\S*)/);
    typeClass = typeClass.length > 0 ? typeClass[1] : null;
    if (typeClass)
    {
      new ta.overlays.RelativeOverlayBelow({
        showCloseButton: true,
        activate: ta.overlays.ACTIVATE_CLICK,
        content: document.getElement('.'+typeClass+'Info').innerHTML
      }, elmt);
    }
  },
  
  /**
   * Create the Vacation Rentals "What to Expect" overlay.
   */
  vrWhatToExpectFlyout:function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: '/vpages/vacation_renters_tips.html'
    }, elmt);
    return false;
  },

  inlineReviewFraudFlyout: function(event, element) {
    if (!element.flyout)
    {
      var g = $('geo');
      var d = $('detail');
      var params = (g || d) ? ("&g="+g.value+"&d="+d.value) : "";
      element.flyout = new Flyout(element, {showArrow: false, offsets:{x:-300, y:15}, remoteContent: '/vpages/fraud.html?lt=evt'+params, flyoutClass: 'fraud'});
      element.flyout.toggle(event);
    }
  },
  
  /**
   * deprecated
   */
  destinationsPopup: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: '/vpages/destinationsFly.html?lt=evt'
      // flyoutClass: 'flyoutMapContents'
    }, elmt);
  },

  /**
   * deprecated
   */
  funStuffPopup: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: '/vpages/funStuffFly.html?lt=evt'
    }, elmt);
  },

  /**
   * Creates the Member Profile overlay.
   */
  memberOverlay: function(evnt, elmt) {
    evnt.stop();
    elmt.onmouseover = null;ta.overlays.Factory.relBelowH_Media
	  var uid = "";
    var catid = "";
	  if (elmt.id.match(/\bUID_([0-9A-FUS-]+)\b/)) {
      uid = RegExp.$1;
    }
    else if (elmt.id.match(/\bCATID_(\d+)\b/)) {
      catid = RegExp.$1;
    }
    new Asset.css(memoverlayCSS);

    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_HOVER,
      pinnable: ta.overlays.PINNABLE_CLICK,
      showCloseButton: true,
      remoteContent: '/MemberOverlay?uid='+uid+'&c='+catid
    }, elmt);
    return false;
  },

  guideOverlay: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    var gid = "";
    if (elmt.id) gid = elmt.id;
	  
    new Asset.css(guideoverlayCSS);
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: '/vpages/travelGuidesFly.html?gid='+gid
    }, elmt);
    return false;
  },

  /**
   * Create Best Value overlay.
   */
  bestValue: function(evnt, elmt) {
    evnt.stop();
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_HOVER,
      remoteContent: '/vpages/bestValueFly.html?log=false'
    }, elmt);
    return false;
  },
  
  recordPropertyTypeClick: function(event, elmt) {
    if (propertyTypeClicked == false) {
      new Ajax("/ActionRecord?action=propertyTypeClicked").request();
      propertyTypeClicked = true;
    }
  }
}
ta.servlet.Inspiration = {

  sortPopularity: function(event, elmt) {
    sort('popularity');
  },

  sortAlphabetical: function(event, elmt) {
    sort('alphabetical');
  }
};
ta.servlet.Reviews = {

  findLink: function(event, elmt) {
    // if link has a class starting with t, take rest of class name
    // as the id of link and follow that link
    if (/\bt([\w\d]+)\b/.test(elmt.className)) window.location = $(RegExp.$1).href;
  },

  mtSubmitOnClick: function(event, elmt) {
    elmt.form.submit();
  }
};

ta.servlet.Location = {

  /**
   * function to switch km distances to mi distances.  Used for nearby locations.
   */
  showMi: function() {
    $$('#NEARBY .milesOption').each(function(item, index)
    {
      item.checked = true;
    });
        
    $$('#NEARBY .milesLabel').each(function(item, index)
    {
      $(item).addClass('selected');
    });
                                        
    $$('#NEARBY .kmLabel').each(function(item, index)
    {
      $(item).removeClass('selected');
    });
    
    $$('#NEARBY .distanceMiles').show();
    $$('#NEARBY .distanceKm').hide();
    Cookie.set(distanceUnitsCookieName, 0);
  },

  /**
   * function to switch mi distances to km distances.  Used for nearby locations.
   */
  showKm: function() {
    $$('#NEARBY .kmOption').each(function(item, index)
    {
      item.checked = true;
    });
    
    $$('#NEARBY .milesLabel').each(function(item, index)
    {
      $(item).removeClass('selected');
    });

    $$('#NEARBY .kmLabel').each(function(item, index)
    {
      $(item).addClass('selected');
    });	
    
    $$('#NEARBY .distanceMiles').hide();
    $$('#NEARBY .distanceKm').show();
    Cookie.set(distanceUnitsCookieName, 1);
  }
}
ta.servlet.Member = {

  memberBenefitsSubscribe: function(event) {
    processMOSubscribeRequest(event);
    return false;
  },
  
  memberGuideRequest: function(event) {
    processGuideRequest(event);
  },

  memberTOGForm: function(event) {
    processTOGForm(event);
  }

};
ta.servlet.Misc = {
  
  /**
   * callback for CIV selector on FunStuff page
   */
  civRedirOnChange: function(event, element) {
    element = $('civSelect');
    ta.servlet.Misc.openValueInNew.apply(element, [ event ]);
    return false;
  },

  /**
   * callback for LP selector on FunStuff page
   */
  lpRedirOnChange: function(event, element) {
    element = $('localPicksSelect');
    ta.servlet.Misc.openValueInNew.apply(element, [ event ]);
    return false;
  },

  /**
   * get value of a select and use it to open a new window
   */
  openValueInNew: function(e) {
    // call bound to a SELECT
    var uri = this.options[this.selectedIndex].value;
    if (uri.length == 0) return;
    var parts = uri.match(/(\d+):(.*)/);
    if (parts) {
      setPID(parseInt(parts[1]));
      uri = parts[2];
    }
    var w = window.open(uri, "p"+window.name+(popupIndex++));
    if (w != null)
    {
      w.opener = self;
      w.focus();
    }
  }

};
ta.servlet.Tourism = {

  bbOfferClicked: function(event, element) {
    event.preventDefault();
    element = $(element);
    if (element.getTag() != 'tr' || !element.hasClass('offerRow')) {
      element = element.getParent('tr.offerRow');
    }
    offerLink = element.getElement('a');
    popCommFN = linkMap['js_popComm'];
    popCommFN(offerLink,event);
  }

};
/**
 * Popup related functions.
 * @namespace
 */
ta.servlet.Popup = {
  index: 0,

  /**
   * Opens a popup window. Note that the allOptions option takes precedense over noScroll.
   * @param {String} url Location to open in the new window
   * @param {String} [name] Name of the new window
   * @param {integer} [width] Width of the new window
   * @param {integer} [height] Height of the new window
   * @param {integer} [x] X position
   * @param {integer} [y] Y position
   * @param {boolean} [noScroll] If false, set turns all options off, if true only scroll bars are enabled
   * @param {boolean} [allOptions=false] If true all options are turned on, false uses browser defaults
   */
  open: function(url, name, width, height, x, y, noScroll, allOptions)
  {
    var ops = "";
    if (url.indexOf("p=HotelsCom") > -1) {
      width = Math.max(width, 960);
      height = Math.max(height, window.getHeight(), 600);
    }
    if (width) ops += ",width="+width;
    if (height) ops += ",height="+height;
    if (x) ops += ",screenX="+x+",left="+x;
    if (y) ops += ",screenY="+y+",top="+y;
    if (ops != "") {
      if (allOptions) {
        ops = "toolbar=1,resizable=1,menubar=1,location=1,status=1,scrollbars=1" + ops;
      }
      else {
        ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=" + (noScroll ? 0 : 1) + ops;
      }
    }
    var w = window.open(url, name || "p"+window.name+(ta.servlet.Popup.index++), ops);
    if (w != null)
    {
      try {w.opener = self;} catch (exc) {}
      w.focus();
      return w;
    }
  },

  /**
   * Opens a check rates popup window using the href of the element.
   */
  checkrates: function(evnt, elmt) {
    evnt.preventDefault();
    if (elmt.getTag() != 'a') elmt = elmt.getParent('a');
    ta.servlet.Popup.open(elmt.href, 'cr', 245, 610, 5, 5);
  },

  /**
   * Opens a sponsored check rates window.
   */
  sponsorCheckRates: function(evnt, elmt) {
    evnt.preventDefault();
    if (elmt.getTag() != 'span') elmt = elmt.getParent('span');
    var accomId = elmt.className.match(/id_(\d+)/)[1];
    var srcUrl = "/HotelDateSearch?d=" + accomId + "&fromPop=false&sponsid=" + ta.retrieve('sponsor.id') + '&area=QC_Button_Map&from=HACSearchButton&returnTo=__2F__GMapsLocationController&sponsdet=mapsHacBottom';
    ta.servlet.Popup.open(srcUrl, 'cr', 245, 610, 5, 5);
  },

  /**
   * Opens a terms window.
   */
  termsPopup: function(event, elmt) {
    popup.apply(elmt, [event, 'terms', 300, 300, 30, 25]);
    return false;
  },

  /**
   * applies to a link in a popup window - opens the url in the parent
   * AND closes the popup
   */
  openInParent: function(event, elmt) {
    var w = false;
    if (window.top && window.top.opener) w = window.top;
    else if (window.opener) w = window;
    if (!w) return true;
    var url = '';
    if(elmt.getTag() == 'form') {
      url = elmt.action + '?' + elmt.toQueryString();
    }
    else if (elmt.getTag() == 'a') {
      url = elmt.href;
    }
    else {
      elmt = elmt.getParent('a');
      url = elmt.href;
    }
    if(url != '') {
      if (event) event.preventDefault();
      w.opener.location = url;
      w.close();
    }
  },

  /**
   * Opens an appropriate sized window for a photo gallery.
   * The url of the photo viewer needs to be supplied.
   */
  photoGallery: function(event, elmt) {
    if (elmt.getTag() != 'a'){
        elmt = elmt.getParent('a');
    }
    var url = elmt.className.match(/u_([^ ]*)/)[1];
    
    // Get the photo id for the cookie
    var id = elmt.className.match(/#([0-9]+)/)[1];
    if (!id) {
      id = "1";
    }
    
    // Set a temp cookie to indicate a popup
    Cookie.set("PhotoPop", id, {domain: cookieDomain});  // No duration, will be removed by server

    // Open the window
    var w = ta.servlet.Popup.open(url, 'media', 780, 800);

    // If we have a window then try to display the right photo (if the window was already open it may be wrong)
    if (w && w.showImage) {
      w.showImage(id);
    }

    return false;
  },

   /**
   * Opens an AIM link with larger-than-usual popup window (pool test)
   */
  
  openAIMLink:function(event,elmt)
  {
      new Event(event).preventDefault();
      var url;
      if (elmt.getTag() == 'a') {
         url = elmt.href;
      } else {
    	  return;
      }
      ta.servlet.Popup.open(url,null,980,800);
  },

  /**
   * 
   */
  voviciSurveyPopunder:function(event,elmt)
  {
    new Event(event).preventDefault();
    var url;
    if (elmt.getTag() == 'a') {
      url = elmt.href;
    } else if (elmt.getParent().getTag() == 'a'){
       url = elmt.getParent().href;
    } else {
       return;
    }
    
    //  record the action and pid
    new Ajax("/ActionRecord?action=AcceptSatisfactionSurvey&pid=1798").request();
    
    //  supress all other popunders for the rest of the session
    var commercePopunder = Cookie.getRaw('CommercePopunder');
    if (!commercePopunder || (commercePopunder != 'SuppressAll'))
    {
      Cookie.set("CommercePopunder", "SuppressAll", {domain: cookieDomain}); //  no duration = session cookie
    }

    ta.servlet.Popup.voviciPopunderLaunch(url);
  },
  
  voviciPopunderLaunch:function(voviciUrl)
  {
    var popunderSlots = ta.retrieve("checkrates.popunderSlots");
    if(!popunderSlots)
    {
      ta.commerce.CROverlay.makePopunderSlots();
      popunderSlots = ta.retrieve("checkrates.popunderSlots");
    }
    var popunderslot = ta.commerce.CROverlay.getOpenSlot(0, popunderSlots);

    var surveyWindow = window.open("", 'voviciSurvey', "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=1,width="+popunderslot.tw+",height="+popunderslot.th+",left="+popunderslot.tx+",top="+popunderslot.ty);
    if(surveyWindow && ta.commerce.CROverlay.isTAWindow(surveyWindow) )
    {
      surveyWindow.blur();
      window.focus();
      surveyWindow.location = voviciUrl;
      popunderslot.occupied = surveyWindow;
      surveyWindow.opener = self;
      surveyWindow.moveTo(popunderslot.x, popunderslot.y);
      surveyWindow.resizeTo(780,670);
    }
    else if( surveyWindow ) //window already used for commerce
    {
      var unusedPopunderCount = ta.retrieve("checkrates.unusedPopunderCount");
      if(!unusedPopunderCount)
      {
        unusedPopunderCount = 0;
      }
      ta.store("checkrates.unusedPopunderCount", ++unusedPopunderCount);
      if(unusedPopunderCount < 20)
      {
        this.voviciPopunderLaunch(voviciUrl);
      }
    }
  }
};
ta.servlet.Hotel = {

  /**
   * Start timer for recording hover over amenity.  Used in amenity bar.
   */
  amenityMouseOver: function(event, elmt) {
    // if we haven't already recorded a hover for this page view...
    if (amenityFiredHoverEvent == false)
    {
      // set a timer at 750ms to record the user hovering
      elmt.hoverTimeout = setTimeout ("recordAmenityIconHover()", 750);
    }
  },

  /**
   * Clear timer for recording hover over amenity.  Used in amenity bar.
   */
  amenityMouseOut: function(event, elmt) {
    // stop the timeout
    clearTimeout (elmt.hoverTimeout);
        
    // remove any references to it
    elmt.hoverTimeout = null;
  },

  detailsTabClicked: function(event, elmt) {
    var tab = $('tabDetails');
    showTab(locationId,'tabDetails', tab);
  },

  photosTabClicked: function(event, elmt) {
    var tab = $('tabPhotos');
    showTab(locationId,'tabPhotos',tab);
  },

  mapTabClicked: function(event, elmt) {
    var tab = $('tabMap');
    showTab(locationId,'tabMap',tab);
  },

  ratingsTabClicked: function(event, elmt) {
    var tab = $('tabRatings');
    showTab(locationId,'tabRatings',tab);
  },
  
  setPid:function(pid) {
    Cookie.set('NPID', pid, {domain: cookieDomain, time: 5});
    return true;
  }

};

/** @namespace */
ta.commerce.CROverlay = {
	
	/**
   * Callback for when the check rates button is pressed.
   */
  Vendor: function(lName, lCheckboxID, lUrl, lVendorName, lAddOffer, lPropId) {
	  this.name = lName;
	  this.checkbox = lCheckboxID;
	  this.url = lUrl;
	  this.window = null;
	  this.lastDates = "";
	  this.slot = null;
	  this.bOpened = false;
	  this.vendorName = lVendorName;
	  this.additionalOffer = lAddOffer;
	  this.propId = lPropId;
	},
	
	createPairedCalendar: function(evnt, elmt){
    new Event(evnt).preventDefault();
    new ta.overlays.PairedCalendar(elmt, {
      validate: true,
      onUpdate: ta.commerce.CROverlay.updateInlineCalendar
    });
	},
	
	updateInlineCalendar: function(paired){
	  var form = $$('#CHECK_RATES_CONT form')[0];
	  if (!form) return;
	  if (!form['calendar']) form.calendar = new ta.overlays.PairedCalendar(form, {
        validate:true,
        onUpdate: ta.commerce.CROverlay.updateInlineCalendar
      });
	  if (form.calendar != paired)
	  {
  	  form.calendar.before.calendar.selectedDate = paired.before.calendar.selectedDate;
  	  form.calendar.after.calendar.selectedDate = paired.after.calendar.selectedDate;
  	  form.calendar.before.updateFields();
  	  form.calendar.after.updateFields();
	  }
	  
	  /* part of second ICR test, can be discarded if no second ICR */
    var syncForm = $$('#CHECK_RATES_CONT_SYNC form')[0];
    if (!syncForm) return;
    if (!syncForm['calendar']) syncForm.calendar = new ta.overlays.PairedCalendar(syncForm, {
        validate:true,
        onUpdate: ta.commerce.CROverlay.updateInlineCalendar
      });
    if (syncForm.calendar == paired) return;
    syncForm.calendar.before.calendar.selectedDate = form.calendar.before.calendar.selectedDate;
    syncForm.calendar.after.calendar.selectedDate = form.calendar.after.calendar.selectedDate;
    syncForm.calendar.before.updateFields();
    syncForm.calendar.after.updateFields();
	},
	
	updateInlineFormElement: function(elmt){
    elmt = $(elmt);
    var elemTag = elmt.getTag().toLowerCase();
    if(elemTag == 'li')
    {
      this._updateInlineFormElement_CB(elmt, false);
    }
    else if(elemTag == 'select')
    {
      this._updateInlineFormElement_Sel(elmt, false);
    }
    else if(elemTag == 'input')
    {
      this._updateInlineFormElement_In(elmt, false);
    }
	},
	
  /**
   * Callback for when the check rates button is pressed.
   */
  checkRatesClicked: function(event, elmt) {
  	ta.commerce.CROverlay.displayVendors(elmt);
  },

  /**
   * Open a series of windows with rate information for each selected vendor 
   */
  displayVendors: function(elmt) {
    
    var crOverlayElem = $E('.overlay');
    if(crOverlayElem && crOverlayElem.overlay && crOverlayElem.overlay.pin)
    {
      crOverlayElem.overlay.pin();
    }
    
  	var vendorSetId = false;
    var vendorSet = false;
    if(vendorSetId = elmt.className.match(/id_(\d+)/))
  	{
  		vendorSetId = vendorSetId[1];
  		var vends = ta.retrieve("checkrates.vendors." + vendorSetId);
      if(vends)
  		{
  			vendorSet = vends;
      }
  	}
  	
    var formElem = elmt.getParent('form');
    if (!vendorSetId || !vendorSet || !this.validateDates(formElem, vendorSetId)) 
    {
    	return;
    }
    else
    {
      formElem.getElement('span.error_msg').hide();
    }
    
    try {
      this.popwindows(elmt, vendorSet, vendorSetId);
      this.addBoomerangTag(elmt, vendorSetId);
    } catch(e) { }
    return false;
  },
  
  validateDates: function(formElem, vendorSetId) {
    //FIX FOR _POP
  	var checkInElem = $('checkIn_' + vendorSetId);
  	var checkOutElem = $('checkOut_' + vendorSetId);
	  // doesn't have calendar, nevermind...
	  if (!formElem || !checkInElem || !checkOutElem) return true;
	  
    if( !checkInElem.value || checkInElem.value.match(/([A-Za-z])/) || 
	      !checkOutElem.value || checkOutElem.value.match(/([A-Za-z])/) )
	  {
	    formElem.getElement('span.error_msg').setContent(sInvalidDates).show();
	    return false;
	  }
	  // never used the calendar, assume defaults are OK
	  if (!checkInElem.calendar || !checkOutElem.calendar) return true;
	  var valid = true;
	  var msg;
	
	  if ((msg = validateDate(checkInElem.calendar)) != null || (msg = validateDate(checkOutElem.calendar)) != null) {
	    valid = false;
	    formElem.getElement('span.error_msg').setContent(msg).show();
	  }
  	else
  		if (!(checkOutElem.calendar.selectedDate >= checkInElem.calendar.selectedDate)) {
	    valid = false;
	    formElem.getElement('span.error_msg').setContent(sInvalidDates).show();
	  }
	  return valid;
	},
  
  popwindows: function (trigger, vendorSet, vendorSetId)
	{
	  var numChecked = 0;
	  var numToOpen = 0;
	  var numOpened = 0;
	  var numOpen = 0;
	  var numCheckedOrOpen = 0;
	  
	  for (var i=0; i<vendorSet.length; i++)
	  {
	    var venCB = $(vendorSet[i].checkbox);
	    if(!venCB)
	    {
	      venCB = $(vendorSet[i].checkbox + "_pop");
	    }
	    if (venCB && venCB.checked)
	    {
	      numChecked++;
	      if (vendorSet[i].window == null)
	      {
	        numToOpen++;
	        numCheckedOrOpen++;
	      } 
	      else if (vendorSet[i].window.closed)
	      {
	        vendorSet[i].window = null;
	        numToOpen++;
	        numCheckedOrOpen++;
	      } 
	      else 
	      {
	        numOpen++;
	        numCheckedOrOpen++;
	      }
	    } 
	    else if (vendorSet[i].window != null && !vendorSet[i].window.closed)
	    {
	      numCheckedOrOpen++;
	    }
	  }
	  
	  if (numChecked == 0)
	  {
	    alert(js_0001); // "Please select at least one vendor from the list."
	    return false;
	  }
	  
	  var sUserData = this.getUserData(vendorSetId);
	  if(!sUserData) {return false;}

	  if($(trigger).hasClass('providerWin') && window.bestValueParentId)
	  {
	    var sAsyncHacUrl = '/HACSearch?seen=1&geo=' + window.bestValueParentId + '&' + sUserData;
	  }
	    	  
	  var pidElmt = $('hacPid');
	  if(pidElmt)
	  {
	    var pid = pidElmt.value;
	    sUserData += "&pid=" + pid;
	  }
	  
	  this.cr_checkDates(sUserData, vendorSet);
	  if($(trigger).hasClass('providerWin'))
	  {
	    // Disable this HAC request for now
	    //if ($('BODYCON').hasClass('poolR') && cookieDomain == '.tripadvisor.com' && ipCountryId == '191' && window.bestValueParentId)
            //{
	    // new Ajax(sAsyncHacUrl).request();
            //}
	  
	    ta.commerce.commerceHelper.openProviderWindow(trigger);
	    if(js_0014)
	    {
	      $("msgbox").hide().empty();
	    }
	    return false;
	  }
	  this.popAll(trigger, sUserData, vendorSet);
	  this.setUpdateText();
    
	  this.focusSlots();
	
	  return true;
	},

  addBoomerangTag: function(elmt, vendorSetId) {
    var formElem = elmt.getParent('form');
    if (!formElem || !vendorSetId) {return;}
    var formdata = {};
  	var checkInElem = $('checkIn_' + vendorSetId);
  	var checkOutElem = $('checkOut_' + vendorSetId);
    var adultsElem = $('qcAdults_' + vendorSetId);
    if (checkInElem) {formdata.checkIn = checkInElem.value;}
    if (checkOutElem) {formdata.checkOut = checkOutElem.value;}
    if (adultsElem) {formdata.adults = adultsElem.value;}
    var ops = {
      data: formdata
    }
    ops.onComplete = function(text, xml)
    {
      var divTag = $('BOOMERANGTAG');
      if (!divTag) {
         divTag = new Element('div', { 'id': 'BOOMERANGTAG' });
         divTag.injectInside($E('body'));
      }
      if (!divTag) {return;}
      divTag.innerHTML = text;
    }
    new Ajax('/HotelCheckRates?Action=AddBoomerangTag', ops).request();
  },
	
	getUserData: function(vendorSetId)
	{
		// User Supplied Dates
	  if(!$('qcInMonth_'+vendorSetId))
	  {
	    vendorSetId = vendorSetId + "_pop";
	  }
	  var sInMonthYear = $('qcInMonth_'+vendorSetId).value.split(/\//);
	  var dCheckin = new Date( sInMonthYear[1], sInMonthYear[0]-1 , $('qcInDay_'+vendorSetId).value );
	  var sOutMonthYear = $('qcOutMonth_'+vendorSetId).value.split(/\//);
	  var dCheckout = new Date(sOutMonthYear[1], sOutMonthYear[0]-1, $('qcOutDay_'+vendorSetId).value);
	  // Today; Force to midnight
	  var dToday = new Date();
	  dToday.setHours(0,0,0,0);
    
	  // Calendar protects against this but keep it anyway
	  if (dCheckin < dToday || dCheckout < dToday) {
	    alert(js_0002); // "Please choose dates in the future."
	    return false;
	  }
	
	  if (dCheckin >= dCheckout) {
	    alert(js_0003); // "Please choose a check-out date that is at least one day later than your check-in date."
	    return false;
	  }
	
	  var dTooFar = new Date((new Date()).getTime() + (330 * 86400000));
	  if (dCheckin > dTooFar || dCheckout > dTooFar) {
	    alert(js_0004); // "Please choose dates that are less than 330 days away."
	    return false;
	  }
	
	  $("msgbox").setContent(js_0005).show(); // "Searching for deals ... this may take a few moments"
	
	  return (
	          "inMonth=" + escape(sInMonthYear[0] + " " + sInMonthYear[1]) +
	          "&inDay=" + escape($('qcInDay_'+vendorSetId).value) +
	          "&outMonth=" + escape(sOutMonthYear[0] + " " + sOutMonthYear[1]) +
	          "&outDay=" + escape($('qcOutDay_'+vendorSetId).value) +
	          "&adults=" + escape($('qcAdults_'+vendorSetId).selectedIndex+1)
	        );
	},
	
	popAll: function(trigger, sUserDataVar, vendorSet)
	{
	  for (var i=0; i<vendorSet.length; i++)
	  {
	  	var venCB = $(vendorSet[i].checkbox);
	  	if(!venCB)
	  	{
	  	  venCB = $(vendorSet[i].checkbox + "_pop");
	  	}
	    if (venCB && venCB.checked)
	    {
	      if (vendorSet[i].bOpened == false || vendorSet[i].window == null || vendorSet[i].window.closed)
	      {
	        if(!this.popOne(i, sUserDataVar, vendorSet, trigger))
	        {
	          this.storeCheckBoxValues(vendorSet);
	          var absOverlay = new ta.overlays.AbsoluteOverlay({
	          	style: 'commerceIEOverlay',
	          	backdrop: ta.overlays.BACKDROP_ALWAYS
	          }, trigger);
	          absOverlay.loadRemoteSuccess = function(txt) {
	            this.inner.setHTML(txt);
					    ta.commerce.CROverlay.popBlockerOverlay(absOverlay, vendorSet, trigger);
					    this.position();
					  };
	          absOverlay.loadRemoteContent('ABS:vpages/quickcheck_ie.html');
	            
	          return false;
	        }
	      }
	    }
	  }
	  return true;
	},
	
	popOne: function(nIndex, sUserDataVar, vendorSet, trigger)
	{
	  try {
	    var bReturn = false;
	    var bHasWindow = (vendorSet[nIndex].window != null && !vendorSet[nIndex].window.closed);
	    var sFinalUrl = vendorSet[nIndex].url + '&' + sUserDataVar;
	    sFinalUrl = this.setCustomArea(sFinalUrl, trigger);
	    sFinalUrl = this.setFromServlet(sFinalUrl);
      var slot;
	    if (bHasWindow)
	    {
	      slot = vendorSet[nIndex].slot;
	    }
	    else
	    {
	      var slotSet = ta.retrieve("checkrates.slots");
	      if(!slotSet)
	      {
	      	this.makeSlots();
	      	slotSet = ta.retrieve("checkrates.slots");
	      }
	      slot = this.getOpenSlot(0, slotSet);
	    }
	    var resusePopunder = null;
	    var popunderSlots = ta.retrieve("checkrates.popunderSlots");
	    if(!popunderSlots)
		  {
		    this.makePopunderSlots();
		    popunderSlots = ta.retrieve("checkrates.popunderSlots");
		  }
		  for(var j = 0; j < popunderSlots.length; j++)
	    {
	      if(popunderSlots[j].occupied)
	      {
	        resusePopunder = popunderSlots[j].occupied;
          popunderSlots[j].occupied = null;  
          break;
	      }
	    }
	     
	    var popWidth = slot.w;
	    if(vendorSet[nIndex].vendorName == "hotels.com")
	    {
	      popWidth = 975;
	    }
	    
	    var winName = vendorSet[nIndex].name;
	    if(resusePopunder)
	    {
	      winName = resusePopunder.name;
	      if(resusePopunder.popunderRepos)
	      {
	        resusePopunder.popunderRepos(slot);
	      }
	    }
	    
	    var sProps = 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,copyhistory=yes,resizable=yes,alwaysraised=true';
	    var sMyProps = sProps + ',screenX=' + slot.x + ',screenY=' + slot.y + ',left=' + slot.x + ',top=' + slot.y + ',height=' + slot.h + ',width=' + popWidth;
	    
	    vendorSet[nIndex].window = window.open(sFinalUrl, winName, sMyProps);
	    
	    if(typeof vendorSet[nIndex].window != "undefined" && vendorSet[nIndex].window != null)
	    {
	      slot.occupied = vendorSet[nIndex].window;
	      vendorSet[nIndex].slot = slot;
	      vendorSet[nIndex].window.focus();
	      vendorSet[nIndex].bOpened = true;
	      vendorSet[nIndex].lastDates = sUserDataVar;
	      bReturn = true;
	    }
	    
	  } catch(e) { }
	
	  return bReturn;
	},
	
	storeCheckBoxValues: function(vendorSet) {
	  var checkBoxSet = new Object();
	  for( x = 0; x < vendorSet.length; x++ ){
      var vCheckBox = $(vendorSet[x].checkbox);
      checkBoxSet[vendorSet[x].name] = vCheckBox ? vCheckBox.checked: false;
    }
    var locStart = vendorSet[0].checkbox.indexOf('_');
    if(locStart != -1)
    {
      var locId = vendorSet[0].checkbox.substring(locStart+1);
      ta.store('checkBoxSet_' + locId, checkBoxSet);
    }
	},
	
	setCustomArea: function(sFinalUrl, trigger) {
		var customArea = "QC_Inline";
		if(customArea = trigger.className.match(/custom_area_(\w+)/))
    {
      customArea = customArea[1];
    }
    return sFinalUrl.replace(/&area\=\w+/, '&area=' + customArea);
  },
  
  setFromServlet: function(sFinalUrl) {
    if(crPageServlet)
    {
      return sFinalUrl.replace(/&from\=\w+/, '&from=HotelDateSearch_' + crPageServlet); 
    }
    return sFinalUrl;
  },
	
	cr_checkDates: function(sUserDataVar, vendorSet) {
		var vendorSetId = vendorSet[0].propId;
	  var oldUserData = ta.retrieve("checkrates.sUserData." + vendorSetId);
	  if(oldUserData && oldUserData != sUserDataVar)
	  {
	    for( x = 0; x < vendorSet.length; x++ ){
	      vendorSet[x].bOpened = false;
	    }
	  }
	  ta.store("checkrates.sUserData." + vendorSetId, sUserDataVar);
	},
	
	setUpdateText: function()
	{
	  // TODO: not a button anymore, an image
	  //document.HotelDateSearch.submitbtn.value=js_0011; // "Update"
	  $("msgbox").hide().empty();
	},
	
	launchHoldingWindow: function(elmt)
	{
		if(elmt.className.match(/popEnabled/))
    {
      var vendorSetId = false;
      if(vendorSetId = elmt.id.match(/check\w+_(\d+)/))
      {
      	vendorSetId = vendorSetId[1];
      }
      if(!vendorSetId)
      {
      	return false;
      }
      var hwURI = ta.retrieve("checkrates.holdingWindowURI");
      if($defined(hwURI))
      {
      	this.popunderLaunch(hwURI, vendorSetId);
      }
    }
	},
	
	popunderLaunch: function(popunder_uri, vendorSetId)
	{
	  //vendor check - only call popunder if there is a vendor to match it, we get a free pop with the click, 
	  //so we only need to prefill up to one less than the vendor count
	  
	  var checkedVendors = 0;
	  var vends = ta.retrieve("checkrates.vendors." + vendorSetId);
	  if(!vends)
	  {
	  	return false;
	  }
	  for( x = 0; x < vends.length; x++ ){   
	      var vendCB = $(vends[x].checkbox);
	      if(!vendCB)
        {
          vendCB = $(vends[x].checkbox + "_pop");
        }
	      if(vendCB && vendCB.checked)
	      {
	        checkedVendors++;
	      }
	  }
	  var usedPopunders = 0;
	  var popunderSlots = ta.retrieve("checkrates.popunderSlots");
	  if(!popunderSlots)
	  {
	    this.makePopunderSlots();
	    popunderSlots = ta.retrieve("checkrates.popunderSlots");
	  }
	  for(var j = 0; j < popunderSlots.length; j++)
	  {
	    if(popunderSlots[j].occupied)
	    {
	      usedPopunders++;
	    }
	  }
	  
	  if( usedPopunders > vends.length - 2 || usedPopunders > checkedVendors - 2)
	  {
	    return;
	  }
	  
	  //vendor check complete
	  var popunderslot = this.getOpenSlot(0, popunderSlots); 
	  if(popunderslot)
	  {
	    var unusedPopunderCount = ta.retrieve("checkrates.unusedPopunderCount");
	    if(!unusedPopunderCount)
	    {
	    	unusedPopunderCount = 0;
	    }
	    var windowName = "UPC_" + unusedPopunderCount;
	    ta.store("checkrates.unusedPopunderCount", ++unusedPopunderCount);
	    var cpu_win = window.open("", windowName, "toolbar=1,location=1,directories=1,status=1,menubar=1,resizable=1,copyhistory=1,scrollbars=1,width="+popunderslot.tw+",height="+popunderslot.th+",left="+popunderslot.tx+",top="+popunderslot.ty);
	    if(cpu_win && this.isTAWindow(cpu_win) )
	    {
	      cpu_win.blur();
	      window.focus();
	      cpu_win.location = popunder_uri;
	      popunderslot.occupied = cpu_win;
	      cpu_win.opener = self;
	      cpu_win.moveTo(popunderslot.x, popunderslot.y);
	      cpu_win.resizeTo(popunderslot.w,popunderslot.h);
	    }
	    else if( cpu_win && unusedPopunderCount < 20) //window already used for commerce
	    {
	      this.popunderLaunch(popunder_uri, vendorSetId);
	    }
	  }
	},
	
	Slot: function (lx, ly, lw, lh){
	  this.x = lx;
	  this.y = ly;
	  this.w = lw;
	  this.h = lh;
	  this.occupied = null;
	},
	
	makeSlots: function (){
	  var nW = 525;
	  var nH = 460;
	  var nXI = 24;
	  var nYI = 24;
	  //move all the pop windows away from the dhtml popup
	  var nX = 500;
	  var nY = (window.screenTop || window.screenY);
	
	  if(screen.width > 1024){
	    nW = 800;
	    nH = 600;
	    nXI = 80;
	    nYI = 40;
	  } else if(screen.width > 800){
	    nW = 620;
	    nH = 500;
	    nXI = 60;
	    nYI = 30;
	  }
	  var slots = [];
	  for (var i=0; i<20; i++){
	    slots[i] = new this.Slot(nX,nY,nW,nH);
	    nX = nX + nXI;
	    nY = nY + nYI;
	  }
	  ta.store("checkrates.slots", slots);
	},
	
	PopunderSlot: function (x, y, w, h, tx, ty, tw, th){
	  this.x = x;
	  this.y = y;
	  this.w = w;
	  this.h = h;
	  this.tx = tx;
	  this.ty = ty;
	  this.tw = tw;
	  this.th = th;
	  this.occupied = null;
	},
	
	makePopunderSlots: function (){
	  var w = 1;
	  var h = 1;
	  var nW = 620;
	  var nH = 500;
	  var nXI = 24;
	  var nYI = 24;
	  if (window.ie7) {w = 250; h = 100;}
	  if (window.webkit) { w = 85; h = 100;}
	  var nX = (window.screenLeft || window.screenX);
	  var nY = (window.screenTop || window.screenY);
	
	  if (typeof(nX) == "undefined") { // full screen
	    nX = 0;
	    nY = 0;
	    var x = nX + window.screen.availWidth - w; // bottom-right corner of window
	    var y = nY + window.screen.availHeight - h;
	  }
	  else{
	    var x = nX + window.getWidth() - w; // bottom-right corner of window
	    var y = nY + window.getHeight() - h;
	  }
	
	  if(window.ie7)
	  {
	    y = y - 95;
	    x = x + 13;
	  }
	  else if(window.webkit)
	  { 
	    y = y - 16;
	  }
	  else
	  {
	    x = 5000;
	    y = 5000;
	  }
	  
	  var popunderSlots = [];
	  var popunderSlotCount = 2;
	  
	  for (var i=0; i<popunderSlotCount; i++){
	    popunderSlots[i] = new this.PopunderSlot(nX, nY, nW, nH, x, y, w, h);
	    nX = nX + nXI;
	    nY = nY + nYI;
	  }
	  ta.store("checkrates.popunderSlots", popunderSlots);
	},
	
	getOpenSlot: function (n, slotGroup){
	  if(n<=0){
	    n=1;
	  }
	  for (var i=n-1; i>=0; i-=1){
	    if (slotGroup[i].occupied == null){
	      return slotGroup[i];
	    } else if (slotGroup[i].occupied.closed){
	      slotGroup[i].occupied = null;
	      return slotGroup[i];
	    }
	  }
	  for (var i=n; i<slotGroup.length; i++){
	    if (slotGroup[i].occupied == null){
	      return slotGroup[i];
	    } else if (slotGroup[i].occupied.closed){
	      slotGroup[i].occupied = null;
	      return slotGroup[i];
	    }
	  }
	  return null;
	},
	
	focusSlots: function(){
		var slotSet = ta.retrieve("checkrates.slots");
    if(!slotSet)
    {
    	this.makeSlots();
    	slotSet = ta.retrieve("checkrates.slots");
    }
	  for (var i=0; i<slotSet.length; i++){
	    if (slotSet[i].occupied != null && !slotSet[i].occupied.closed)
	    {
	      try {
	        slotSet[i].occupied.focus();
	      } catch(e) { }
	    }
	  }
	},

	isTAWindow: function(win_hook){
	  var isTA = true;
	  try {
	    var tempLoc = win_hook.location;
	    // for some reason IE runs the next line even if the last one throws a SecurityException
	    window.status = ""; // so do something pointless - DO NOT REMOVE THIS LINE!
	
	    // Handle case when IE7 returns '' for window.opener.location rather than a security exception
	    // bug 28404   gbelote
	    if (tempLoc == null || tempLoc == "")
	    {
	        isTA = false;
	    }
	  }
	  catch (e) {
	    isTA = false;
	  }
	  return isTA;
	},
	
	popunderReminder: function(pop_hook){
		var popunderSlots = ta.retrieve("checkrates.popunderSlots");
	  if(!popunderSlots)
	  {
	  	ta.commerce.CROverlay.makePopunderSlots();
	  	popunderSlots = ta.retrieve("checkrates.popunderSlots");
	  }
    for(var j = 0; j < popunderSlots.length; j++)
    {
      if(!popunderSlots[j].occupied)
      {
        popunderSlots[j].occupied = pop_hook;
        break;
      }
    }
	},
	
	buildInnerContent: function(ajaxContent, vendorSet)
	{
		return ajaxContent;
	},
  
	popBlockerOverlay: function(crOverlay, vendorSet, oldTrigger) {
	  if(pageServlet == 'Hotels')
	  {
	    if(crOverlay && crOverlay.inner)
	    {
  	    crOverlay.inner.getElement('div.legend').show();
  	    if(typeof selectedHotelName != "undefined")
  	    {
  	      crOverlay.inner.getElement('div.accomName').innerHTML = selectedHotelName;
  	    }
	    }
	  }
	  else
	  {
	    $('QUICK_CHECK_LB').getElement('h2.popBlock').show();
	  }
	  
	  //update total counts
	  var vendors = vendorSet;
	  var openedVend = 0;
	  var totVend = 0;
	  var openedAddVend = 0;
	  var totAddVend = 0;
	  
	  var parentOverlay = $(crOverlay.source).findParentByClass('commerceOverlay');
	  // if parentOverlay not found, form is inline
	  if(!parentOverlay)
	  {
	    parentOverlay = $(crOverlay.source).getParent('form');
	  }
	  
	  for( x = 0; x < vendors.length; x++ ){
	    if(this._isCheckBoxChecked(vendors[x], parentOverlay))
	    {
	      if(vendors[x].additionalOffer)
	      {
	        totAddVend++;
	      }
	      else
	      {
	        totVend++;
	      }
	      
	      if(vendors[x].bOpened)
	      {
	        if(vendors[x].additionalOffer)
	        {
	          openedAddVend++;
	        }
	        else
	        {
	          openedVend++;
	        }
	        
	      }
	    }
	  }
	  
	  $E('.cs_ps', '#QUICK_CHECK_LB').getElement('.opnSites').innerHTML = openedVend;
    $E('.cs_ps', '#QUICK_CHECK_LB').getElement('.totSites').innerHTML = totVend;
    $E('.cs_ao', '#QUICK_CHECK_LB').getElement('.opnSites').innerHTML = openedAddVend;
    $E('.cs_ao', '#QUICK_CHECK_LB').getElement('.totSites').innerHTML = totAddVend;
	  if(totVend == 0)
	  {
	    $('QUICK_CHECK_LB_PARTNERS').addClass('qcl_hidden');
	    $E('.cs_ps', '#QUICK_CHECK_LB').addClass('qcl_hidden');
	  }
	
	  if(totAddVend == 0)
	  {
	    $('QUICK_CHECK_LB_ADD_OFFERS').addClass('qcl_hidden');
	    $E('.cs_ao', '#QUICK_CHECK_LB').addClass('qcl_hidden');
	  }
	  
	  var normOffers = 0;
	  var addOffers = 0;
	  
	  var customArea = "QC_Inline";
    if(customArea = oldTrigger.className.match(/custom_area_(\w+)/))
    {
      customArea = customArea[1];
    }
	  
	  for( x = 0; x < vendors.length; x++ ){
	    if(this._isCheckBoxChecked(vendors[x], parentOverlay))
	    {
	      
	      var listSectionId = 'QUICK_CHECK_LB_PARTNERS';
	      if(vendors[x].additionalOffer)
	      {
	        listSectionId = 'QUICK_CHECK_LB_ADD_OFFERS';
	        addOffers++;
	      }
	      else
	      {
	        normOffers++;
	      }
	      var lnkUrl = vendors[x].url;
	      var elemAnchor = new Element('a', {'id' : 'qc_'+x , 'href' : lnkUrl})
	        .injectInside(new Element('div', {'class': 'qc_lb_name'})
	          .injectInside($(listSectionId)) ).setContent(vendors[x].vendorName);
	      var imgUrl = vendors[x].bOpened ? cr_loc_vend_ch : cr_loc_vend;
	      elemAnchor.setStyle('background', 'url(' + imgUrl + ') no-repeat');
	      elemAnchor.addClass('custom_area_' + customArea);
	      
	      elemAnchor.addEvent('click', function(evnt){
	        var e = new Event(evnt || window.event);
  				e.preventDefault();
	        var trigger = $(e.target);
	        var clickedVenderName = this.innerHTML;
	        
	        //update opened status of the popped vendor
	        var iPopVendor = -1;
	        var unopenedVendors = false;
	        for( var x = 0; x < vendors.length; x++ ){
	          if(vendors[x].vendorName == clickedVenderName)
	          {
	            if(!vendors[x].bOpened)
			        {
			          $(trigger).setStyle('background', 'url(' + cr_loc_vend_ch + ') no-repeat');
			          iPopVendor = x;
			          var offerSection = vendors[x].additionalOffer ? 'div.cs_ao span.opnSites' : 'div.cs_ps span.opnSites';
			          var openSitesElem = $('QUICK_CHECK_LB').getElement(offerSection);
			          var openSites = parseInt(openSitesElem.innerHTML);
					      openSitesElem.innerHTML = openSites+1;
					      vendors[x].bOpened = true;
					    }
					    var oldUserData = ta.retrieve("checkrates.sUserData." + vendors[0].propId);
	            ta.commerce.CROverlay.popOne(x, oldUserData, vendorSet, trigger);
	          }
	          
	          if(ta.commerce.CROverlay._isCheckBoxChecked(vendors[x], parentOverlay) && !vendors[x].bOpened)
					  {
					    unopenedVendors = true;
					  }
	        }
	        
		    if(!unopenedVendors)
	        {
		      //if all vendors have popped, reset them and close the lb
		      for( x = 0; x < vendors.length; x++ ){
	            vendors[x].bOpened = false;
		      }
		      crOverlay.hide();
		    }
	
		    ta.commerce.CROverlay.focusSlots();
		    
		    return false;
		  });
	    }
	  }
	  
	  //CheckRates Tip Test IE DHTML
	  if(typeof qcShowTip != 'undefined')
	  {
		  $('QUICK_CHECK_HR').setStyle('display','none');
		  $('QUICK_CHECK_TIP').setStyle('display','block');
		  $('QUICK_CHECK_TIP').setHTML(
		    '<div><span>Tip:</span> Travelers usually check <strong>3 or more sites</strong> to find the best price.</div><img src="'+qcTipImage+'" />'
		  );
	  }
	},
	
	_isCheckBoxChecked: function(vendorObj, parentOverlay){
	  var isChecked = false;
    if(parentOverlay)
	  {
  	  var nodeCol = parentOverlay.getElement('input[id^=' + vendorObj.checkbox + ']');
  	  if(nodeCol)
      {
        isChecked = nodeCol.checked;
      }
      else
      {
        var checkBoxSet = ta.retrieve('checkBoxSet_' + vendorObj.propId);
        isChecked = checkBoxSet[vendorObj.name];
      }
	  }
    return isChecked;
	},
	
	/**
   * List of extensions that can be appended to check rates form elements
   * to trigger syncing with the master ICR box elements
   * NEED TO BE OF LENGTH 4
   */
  crSyncExtensions: new Array('_pop', '_icr'),
	
	updateCalendarFormElement: function(e,year,month,day){
	  var cal = currentCalendar;
	  calendarSelect(e,year,month,day);
	  this.updateInlineFormElement(cal.source);
	  var other = cal.before || cal.after;
	  if(other)
	  {
	    this.updateInlineFormElement(other);
	  }
	},
	
	/*
	 * keep inline CR form element in sync with dhtml version
	 * of the same element
	 * elmt passed in is the dhtml version of element
	 * flip is a toggle to do the opposite (sync the
	 * dhtml with the inline)
	 */
	_updateInlineFormElement_CB: function(elmt, flip){
	  var elemCheckBox = elmt.getElement('input[type=checkbox]');
	  if(elemCheckBox)
	  {
  	  var ext = elemCheckBox.id.substring( elemCheckBox.id.length - 4 );
      if(this.crSyncExtensions.contains(ext))
      {
        var inlineCBName = elemCheckBox.id.substring( 0, elemCheckBox.id.length - 4 );
        var inlineCheckBox = $(inlineCBName);
        if(inlineCheckBox)
        {
          if(flip)
          {
            elemCheckBox.checked = inlineCheckBox.checked;
          }
          else
          {
            inlineCheckBox.checked = elemCheckBox.checked;
            var syncForm = $$('#CHECK_RATES_CONT_SYNC form')[0];
            if(syncForm)
            {
              var syncCB = syncForm.getElement('#' + inlineCBName + "_icr");
              syncCB.checked = inlineCheckBox.checked;
            }
          }
        }
      }
      else
      {
        var inlineCBName = elemCheckBox.id;
        var inlineCheckBox = $(inlineCBName);
        this.crSyncExtensions.each(function(syncExt){
          var syncElem = inlineCBName + syncExt;
          syncElem = $(syncElem);
          if(syncElem)
          {
            syncElem.checked = inlineCheckBox.checked;
          }
        });
      }
	  }
  },
  
  _updateInlineFormElement_Sel: function(elmt, flip){
    var ext = elmt.id.substring( elmt.id.length - 4 );
    if(this.crSyncExtensions.contains(ext))
    {
      var inlineSelName = elmt.id.substring( 0, elmt.id.length - 4 );
      var inlineSelect = $(inlineSelName);
      if(inlineSelect)
      {
        if(flip)
        {
          elmt.selectedIndex = inlineSelect.selectedIndex;
        }
        else
        {
          inlineSelect.selectedIndex = elmt.selectedIndex;
        }
      }
      this._updatedInlineFormElement_Sel_Helper(inlineSelName);
    }
    else
    {
      this._updatedInlineFormElement_Sel_Helper(elmt.id);
    }
  },
  
  _updatedInlineFormElement_Sel_Helper: function(inlineSelName){
    var inlineSelect = $(inlineSelName);
    this.crSyncExtensions.each(function(syncExt){
      var syncElem = inlineSelName + syncExt;
      syncElem = $(syncElem);
      if(syncElem)
      {
        syncElem.selectedIndex = inlineSelect.selectedIndex;
      }
    });
  },
  
  _updateInlineFormElement_In: function(elmt, flip){
    if(elmt && elmt.id.indexOf('_pop') != -1)
    {
      var inlineCalName = elmt.id.substring( 0, elmt.id.length - 4 );
      var inlineCal = $(inlineCalName);
      if(inlineCal)
      {
        if(flip)
        {
          elmt.value = inlineCal.value;
        }
        else
        {
          inlineCal.value = elmt.value;
        }
        var inlineCalLocId = inlineCal.id.match(/_(\d+)/);
        if(inlineCalLocId)
        {
          inlineCalLocId = inlineCalLocId[1];
          var calType = 'qcIn';
          if(elmt.id.indexOf('checkOut') != -1)
          {
            calType = 'qcOut';
          }
          var inlineDay = $(calType + 'Day_' + inlineCalLocId);
          var overlayDay = $(calType + 'Day_' + inlineCalLocId + '_pop');
          if(inlineDay && overlayDay)
          {
            if(flip)
            {
              overlayDay.value = inlineDay.value;
            }
            else
            {
              inlineDay.value = overlayDay.value;
            }
          }
          var inlineMonth = $(calType + 'Month_' + inlineCalLocId);
          var overlayMonth = $(calType + 'Month_' + inlineCalLocId + '_pop');
          if(inlineMonth && overlayMonth)
          {
            if(flip)
            {
              overlayMonth.value = inlineMonth.value;
            }
            else
            {
              inlineMonth.value = overlayMonth.value;
            }
          }
        }
      }
    }
  },

	/*
	 * Function to keep check rates overlays in sync
	 * with the inline check rates box
	 * 'this' inside the function is bound to the overlay
	 */
	commCopyUpdate: function(){
	  if(!this.onReadyFired)
	  {
	    this.onReadyFired = true;
	    this.addEvent('onShow', ta.commerce.CROverlay.commCopyUpdate);
	  }
	  if(this.inner)
	  {
	    var uList = this.inner.getElement('ul');
	    if(uList)
	    {
  	    uList.getElements('li').each(
          function(listItem){ 
            ta.commerce.CROverlay._updateInlineFormElement_CB(listItem, true);
          }
        );
	    }
	    var crSel = this.inner.getElement('select');
      if(crSel)
      {
        ta.commerce.CROverlay._updateInlineFormElement_Sel(crSel, true);
      }
	    var ciCal = this.inner.getElement('input.checkIn');
	    if(ciCal)
	    {
	      ta.commerce.CROverlay._updateInlineFormElement_In(ciCal, true);
	    }
	    var coCal = this.inner.getElement('input.checkOut');
	    if(coCal)
      {
        ta.commerce.CROverlay._updateInlineFormElement_In(coCal, true);
      }
	  }
	},
	
	/**
	 * Pool test: checking an ICR checkbox pops the TPW window
	 * with the label's provider selected
	 */
	crLabelClick: function(evnt, checkbox){
    var corCB = $(checkbox.htmlFor);
	  if(corCB)
	  {
	    corCB.checked = true;
      var e = new Event(evnt || window.event);
      e.stop();
      var topForm = $(corCB).getParent('form');
	    if(topForm)
	    {
	      var corCRButton = $(topForm).getElement('.check img');
	      if(corCRButton)
	      {
	        var corCBAuthor = corCB.id.substr(0, corCB.id.lastIndexOf('_'));
	        ta.commerce.commerceHelper.openProviderWindow(corCRButton, corCBAuthor);
	      }
	    }
	  }
	}
			
  //update commas if you change the final function in this namespace
};
	
	
/**
 * A scrolling, multi-month calendar.
 * @class
 *
 * @option {Date} [selectedDate] Currently selected date
 * @option {Date} [firstDate] First valid date, anything before it will be disabled
 * @option {Date} [lastDate] Last valid date, anything after it will be disabled
 * @option {boolean} [dualCalendar=true] Show two months if true, one if false
 * @option {Array} [invalidDates] List of invalid dates or date ranges.  If it is a date range then we
 *     are expecting a sub-array of size 2.  All these dates will be replaced with a red 'X'.  Note
 *     that dates may be in timestamp format (i.e. number of milliseconds).
 * @option {boolean} [useLinks=true] If each valid date should be a link
 * @option {boolean} [dayNames=false] show abbreviated names if true (e.g. Mon), or letter if false (e.g. M)
 * @option {Function} [formatter] Function to select cell contents for each day (only used when embedded)
 * @option {integer} [offsetOther=0] Padding between months if showing two months (only used when embedded)
 * @option {String} choiceLink name of function to call when user clicks on a date in the calendar
 * @option {String} prevAction name of function to call when user clicks on previous button
 * @option {String} nextAction name of function to call when user clicks on next button
 */
ta.widgets.Calendar = new Class({
  options: {
    selectedDate: null,
    firstDate: null,
    lastDate: null,
    dualCalendar: true,
    invalidDates: null,
    useLinks: true,
    dayNames: false,
    formatter: null,
    offsetOther: 0,
    choiceLink: null,
    prevAction: null,
    nextAction: null
  },

  /**
   * @param {Object} options Options, see below.
   */
  initialize: function(options){
    this.setOptions(options);
    
    if (!this.options.firstDate) this.options.firstDate = new Date();
    if (!this.options.lastDate) this.setLastDate();

    this.today = new Date();
    this.selectedDate = this.options.selectedDate;

    // force Dates to midnight for easier comparison
    this.today.setHours(0,0,0,0);
    if (this.options.firstDate) this.options.firstDate.setHours(0,0,0,0);
    if (this.options.lastDate)  this.options.lastDate.setHours(0,0,0,0);
    if (this.selectedDate)      this.selectedDate.setHours(0,0,0,0);
    
    var arr = [];
    if (this.options.prevAction && this.options.nextAction){
      arr.push('<div class="navCal">');
      arr.push('<span class="prev"><a style="display: block;" onclick="');
      arr.push(this.options.prevAction);
      arr.push('(event, this);">Previous Month</a></span>');
      arr.push('<span class="next"><a style="display: block;" onclick="');
      arr.push(this.options.nextAction);
      arr.push('(event, this);">Next Month</a></span>');
      arr.push('</div>');
    }
    this.container = new Element('div', {'class': 'calendar'});
    this.container.setHTML(arr.join(''));
  },
  
  /**
   * Set the last date to the end of the month, 11 months from today.
   */
  setLastDate: function(){
    var d = new Date();
    d.setDate(1);
    if (d.getMonth() == 0) {
      d.setMonth(11);
    }
    else {
      d.setMonth(d.getMonth()-1);
      d.setFullYear(d.getFullYear()+1);
    }
    this.options.lastDate = d;
  },

  /**
   * Update the month(s) shown by the calendar. Should be called whenever the date is changed externally.
   */
  update: function(){
    if (this.currentMonth) this.removeTable(null, this.currentMonth);
    if (this.nextMonth) this.removeTable(null, this.nextMonth);

    var firstMonth = this.selectedDate || this.today;
    if (this.options.firstDate && firstMonth < this.options.firstDate) firstMonth = this.options.firstDate;
    if (this.options.dualCalendar) {
      var testMonth = new Date(firstMonth);
      testMonth.setDate(ta.util.date.DAYS_IN_MONTH[testMonth.getMonth()]);
      testMonth.setHours(0,0,0,0);
      if (testMonth >= this.options.lastDate) {
        firstMonth = new Date(firstMonth);
        if (firstMonth.getMonth() == 0)
          firstMonth.setFullYear(firstMonth.getFullYear()-1,11,1);
        else
          firstMonth.setMonth(firstMonth.getMonth()-1,1);
      }
    }
    
    this.currentMonth = this.createMonth(firstMonth);
    this.container.appendChild(this.currentMonth.elmt);
    if (this.options.dualCalendar) {
      var d = new Date(this.currentMonth.date);
      var day = d.getDate();
      var nextMonth = d.getMonth()+1;
      if (day > ta.util.date.DAYS_IN_MONTH[nextMonth]) { day = ta.util.date.DAYS_IN_MONTH[nextMonth]; }
      d.setMonth(nextMonth, day);
      this.nextMonth = this.createMonth(d);
      this.container.appendChild(this.nextMonth.elmt);
    }
    
    //  set initial state of prev and next buttons
    this.updatePrevNext();

    return this;
  },
  
  /**
   * Adjust the left offset of the current and next month (if applicable)
   */
  reposition: function(){
    this.currentMonth.elmt.setStyle('left', '0px');
    if (this.options.dualCalendar)
    {
        this.nextMonth.elmt.setStyle('left', this.currentMonth.elmt.getCoordinates().width + this.options.offsetOther + 'px');
    }
    return this;
  },
  
  /**
   * Update the visibility of the prev and next buttons
   */
  updatePrevNext:function(){
    var currentPrev = this.currentMonth.date;
    var currentNext = this.options.dualCalendar ? this.nextMonth.date : this.currentMonth.date;
    
    var hidePrev = ta.util.date.inSameMonth(currentPrev, this.options.firstDate);
    var hideNext = ta.util.date.inSameMonth(currentNext, this.options.lastDate);
    
    getChildByClass(this.container,"prev").style.display = (hidePrev ? 'none' : 'block');
    getChildByClass(this.container,"next").style.display = (hideNext ? 'none' : 'block');
  },
  
  /**
   * Creates a month object; including start/end dates and the Table object.
   * @param {Date} d any date in the month
   */
  createMonth: function(d){
    var month = {date: d};

    // currDate = the date that we use as we step through the month
    var currDate = new Date(d);
    currDate.setDate(1);
    currDate.setHours(0,0,0,0);
    var currentDay = (currDate.getDay() - jsGlobalDayOffset + 7) % 7;
    
    var arr = [];
    var year = d.getFullYear();
    var currentMonth = d.getMonth();
    var now = new Date();
    var today = ta.util.date.inSameMonth(now,d) ? now.getDate() : -1;
    var selectedDay = -1;
    if (this.selectedDate && this.selectedDate.getMonth() == currentMonth)
      selectedDay = this.selectedDate.getDate();
    
    arr.push('<table cellspacing="0" cellpadding="0" border="0"><thead><tr><th class="caption" colspan="7">');
    arr.push(DATE_FORMAT_MMM_YYYY.replace(/MMM/,jsGlobalMonths[currentMonth]).replace(/YYYY/,year));
    arr.push('</th></tr><tr>');
    for (var i = 0; i < jsGlobalDaysShort.length; i++) {
      arr.push('<th>');
      arr.push(this.options.dayNames ? jsGlobalDaysAbbrev[i] : jsGlobalDaysShort[i]);
      arr.push('</th>');
    }
    arr.push('</tr></thead><tbody><tr>');
    
    // blanks at beginning of month
    for (var i = 0; i < currentDay; i++)
      arr.push('<td class="other">&nbsp;</td>')
  
    // days
    currentDay--;
    var day = 0;
    var numDays = ta.util.date.DAYS_IN_MONTH[month.date.getMonth()];
    if (month.date.getMonth() == 1 && month.date.getYear() % 4 == 0) numDays += 1;
    var numValidDays = numDays;
    while (day < numDays) {
      currentDay = (currentDay + 1) % 7;
      day++;
      currDate.setDate(day);
      var contents = day;
      var cname = null;
      var inRange = !(currDate < this.options.firstDate || currDate > this.options.lastDate);
      var isValid = !this.isInvalidDate(currDate);
      var link = this.options.choiceLink;
      
      // determine contents and classname
      if (this.options.formatter) {
        var cnc = this.options.formatter(currDate, inRange, isValid, currentDay, this.options);
        cname = cnc.cname;
        contents = cnc.contents;
        link = $pick(cnc.link, link);
      }
      else if (!isValid) {
        cname = 'invalid';
        contents = 'X';
      }
      else if (day == today) {
        if (day == selectedDay) {
          cname = 'today selected';
        }
        else {
          cname = 'today';
        }
      }
      else if (day == selectedDay) {
        cname = 'selected';
      }
      
      if (currentDay == 0) {
        arr.push('</tr><tr>');
      }
      if (!inRange) {
        arr.push('<td class="');
        arr.push(cname || 'disabled');
        arr.push('">');
        arr.push(contents || day);
        arr.push("</td>");
      }
      else {
        if (cname) {
          arr.push('<td class="');
          arr.push(cname);
          arr.push('">');
        }
        else {
          arr.push('<td>');
        }
        
        if (isValid && this.options.useLinks && link) {
          arr.push('<a href="javascript:void(0);" onclick="');
          arr.push(link);
          arr.push('(event,');
          arr.push(year);
          arr.push(',');
          arr.push(currentMonth);
          arr.push(',');
          arr.push(day);
          arr.push(',this)">');
          arr.push(contents);
          arr.push('</a>');
        }
        else {
          arr.push(contents);
        }
        arr.push('</td>');
      }
    }
    
    // blanks at end of month
    while (++currentDay < 7)
      arr.push("<td class='other'>&nbsp;</td>")
  
    arr.push('</tr></tbody></table>');
    
    month.elmt = new Element('div', {'class': 'month'}).setHTML(arr.join(''));
    
    return month;
  },
  
  /**
   * Check if this is an invalid date given 'this.options.invalidDates'.
   * @param {Date} date date to check
   */
  isInvalidDate: function(date){
    return ta.util.date.inDateSet(date, this.options.invalidDates);
  },
  
  /**
   * Verifies that the selected date of this calendar is equal to or between the first and last dates.
   *
   * @returns {boolean} the validity of the selected date
   */
  isValid: function(){
    if (!this.selectedDate) return true;
    return this.selectedDate.getTime() >= this.options.firstDate.getTime() && this.selectedDate.getTime() <= this.options.lastDate.getTime();
  },

  /**
   * Moves the calendar back one month.
   */
  prev: function(){
    if (this.animating) return;
    var dc = this.options.dualCalendar;
    this.animating = true;
    
    //  calculate the prev month
    var d = new Date(this.currentMonth.date);
    var month = d.getMonth()-1;
    if(month < 0){
        month = month + 12;
        d.setFullYear(d.getFullYear() - 1);
    }
    var day = d.getDate();
    if (day > ta.util.date.DAYS_IN_MONTH[month]) { day = ta.util.date.DAYS_IN_MONTH[month]; }
    d.setMonth(month, day);
    
    //  grab the width pf the current month table
    var w = this.currentMonth.elmt.getCoordinates().width;

    //  create the new month table, offset it to its starting position (to prevent flicker)
    //  and inject it (we don't have the actual width of the new month table yet so assume
    //  it is about the size of the current month... should be good enough to eliminate
    //  the flicker).
    var m = this.createMonth(d);
    m.elmt.style.left = '-' + w + 'px';
    m.elmt.inject(this.container.getElement('.navCal'), 'after');
    
    //  grab the tables and slide them to the right with an animation
    var elmts = this.container.getElements('div.month');
    var opts = {'0':{left:[-w,0]},
                '1':{left:[0,w + this.options.offsetOther]}};
    if (dc) opts['2'] = {left:[w + this.options.offsetOther,w*2 + this.options.offsetOther]};
    
    var fx = new Fx.Elements(elmts, {
      onComplete: this.doneAnimating.bindAsEventListener(this, [this.nextMonth || this.currentMonth])
    });
    
    //  update the current and next month
    if (dc) this.nextMonth = this.currentMonth;
    this.currentMonth = m;
    
    //  update the state of the prev and next buttons
    this.updatePrevNext();

    //  start the animation
    fx.start(opts);
  },

  /**
   * Moves the calendar forward one month.
   */
  next: function(){
    if (this.animating) return;
    var dc = this.options.dualCalendar;
    this.animating = true;
    
    //  calculate the next month
    var d = new Date(this.currentMonth.date);
    var month = d.getMonth() + (dc ? 2 : 1);
    if(month > 11){
        month = month - 12;
        d.setFullYear(d.getFullYear() + 1);
    }
    var day = d.getDate();
    if (day > ta.util.date.DAYS_IN_MONTH[month]) { day = ta.util.date.DAYS_IN_MONTH[month]; }
    d.setMonth(month, day);
    
    //  grab the width pf the current month table
    var w = this.currentMonth.elmt.getCoordinates().width;
    
    //  grab the total width of the calendars (if it is a dual calendar)
    var totalWidth = w + (dc ? this.nextMonth.elmt.getCoordinates().width : 0)

    //  create the new month table, offset it to its starting position (to prevent flicker)
    //  and inject it
    var m = this.createMonth(d);
    m.elmt.style.left = totalWidth + 'px';
    m.elmt.injectInside(this.container);
    
    //  grab the tables and slide them to the left with an animation
    var elmts = this.container.getElements('div.month');
    var opts = {'0':{left:[0,-w]},
                '1':{left:[w + this.options.offsetOther,0]}};
    if (dc) opts['2'] = {left:[w*2 + this.options.offsetOther,w + this.options.offsetOther]};

    var fx = new Fx.Elements(elmts, {
      onComplete: this.doneAnimating.bindAsEventListener(this, [this.currentMonth])
    });

    //  update the current and next month
    if (dc){
        this.currentMonth = this.nextMonth;
        this.nextMonth = m;
    }
    else {
      this.currentMonth = m;
    }
    
    //  update the state of the prev and next buttons
    this.updatePrevNext();

    //  start the animation
    fx.start(opts);
  },

  /**
   * Removes the table from the container. Used by prev/next as a bound event listener for when
   * the animation completes.
   * @param {Event} e event object, unused
   * @param {Element} t element that is the month to be removed
   */
  removeTable: function(e, t){
    t.elmt.remove();
  },

  doneAnimating: function(e, t){
    this.removeTable(e, t);
    this.animating = false;
  },

  /**
   * Called when the user selects a date. Fires any onSelect events.
   * @param {Event} e event object
   * @param {Date} d date the user selected
   */
  select: function(e, d){
    this.selectedDate = d;
    this.fireEvent('onSelect', [this, e]);
  },

  /**
   * Convenience method to get a month near a given month. Checks for boundary conditions.
   * @param {integer} month starting month
   * @param {integer} mod amount to alter the number by (positive or negative)
   */
  nearbyMonth: function(month, mod){
    var d = month + mod;
    if (d > 11) d -= 12;
    else if (d < 0) d += 12;
    return d;
  }
});
ta.widgets.Calendar.implement(new Events, new Options);
/** Never show the backdrop */
ta.overlays.BACKDROP_NEVER  = 0;
/** Always show the backdrop */
ta.overlays.BACKDROP_ALWAYS = 1;

/**
 * Common base class for all floating overlay elements. A overlay is an element that appears on the page
 * above all other elements and is absolutely positioned over the document. This class will
 * automatically place an iframe behind the overlay to block select element in IE6.
 * @class
 *
 * @option {boolean} [showCloseButton=false] Whether or not to show the close button
 * @option {String}  [style=typeO] Class name on container div.
 * @option {boolean} [backdrop=ta.layers.BACKDROP_NEVER] Should be one of ta.layers.BACKDROP_*
 */
ta.overlays.Overlay = new Class({
  options: {
    requestData:      null,
    showCloseButton:  false,
    style:            'typeO',
    backdrop:         ta.overlays.BACKDROP_NEVER,
    isChild:          false,
    remoteContent:    null
  },
  
  /**
   * @param {Object} options Options, see below.
   */
  initialize: function(options, elmt) {
    this.setOptions(options);
    this.source = $(elmt);
    // state flags
    this.visible    = false;
    this.backdropOn = false;
    
    // bind event handlers
    this.showHandler = this.show.bindWithEvent(this);
    this.hideHandler = this.hide.bindWithEvent(this);
    
    // setup container
    this.container = new Element('div', {
      styles: {
        position: 'absolute',
        left: '-999em',
        top: '-999em',
        fontSize: '.75em',
        zIndex: '1001'
      },
      'class': this.options.style + " overlay"
    });
    this.container.overlay = this;
    this.inner = new Element('div', {
      'class': 'inner'
    }).injectInside(this.container);
    
    if (this.options.showCloseButton) {
      this.closeBtn = new Element('div', {
        styles: {
          position: 'absolute',
          fontSize: '92.5%',
          cursor: 'pointer'
        },
        'class': 'close'
      }).inject(this.container);
      this.inner.addClass('withClose');
    }
    
    // IE6 requires an IFrame shim
    if (window.ie6) {
      this.shim = new Element('iframe', {
        styles: {
          position: 'absolute',
          left: '-999em',
          top: '-999em',
          border: 'none'
        }
      });
    }
    
    if (this.options.content) {
      this.inner.setHTML(this.options.content);
    }
    else if (this.options.remoteContent) {
      this.loadRemoteContent(this.options.remoteContent);
    }

    this.container.addEvent('trash', this.destroy.bind(this));
  },

  destroy: function(){
    this.container.overlay = null;
  },
  
  /**
   * Positions the container.
   */
  position: function() {
  	this.positionShim();
  },
  
  /**
   * Positions the IFrame shim if necessary.
   */
  positionShim: function() {
  	if (!this.shim) return;
    // a small delay is needed in case the popup styles change its size
    (function(){
      this.shim.injectBefore(this.container);
      var c = this.container.getCoordinates();
      this.shim.setStyles({
        left: c.left,
        top: c.top,
        width: c.width,
        height: c.height,
        zIndex: this.container.getStyle('z-index') - 1
      });
    }).delay(10, this);
  },
  
  /**
   * Adds the container to the DOM and positions it.
   * @param {Event} evnt The event
   */
  show: function(evnt) {
    if (this.visible) return;
    if (evnt) new Event(evnt).preventDefault();
    
    // it is possible for an overlay to spawn multiple child overlays, so find other overlays and
    // remove them if they aren't the parent overlay
    // or if they have not been flagged as permanent (like the dhtml popups) - see bug 40975.
    $$('.overlay').each(function(other){
      if (other.overlay == this) return;
      if (this.parentOverlay && other.overlay == this.parentOverlay) return;
      if (other.overlay instanceof ta.overlays.PermanentOverlay) return;
      other.overlay.hide(evnt);
    }, this);
    
    $(document.body).adopt(this.container);
    this.position();
    this.positionShim();
    if (this.options.backdrop == ta.overlays.BACKDROP_ALWAYS) this.enableBackdrop();
    this.visible = true;
    this.fireEvent('onShow', this);
    return this;
  },
  
  /**
   * Removes the container from the DOM.
   * @param {Event} evnt The event
   */
  hide: function(evnt) {
    if (window.flyout) window.flyout.hide();
    if (this.backdropOn) this.disableBackdrop();
    if (!this.container.inDocument()) return this;
    if (this.shim) {
      this.shim.setStyles({
        left: '-999em',
        top: '-999em'
      });
      if (this.shim.inDocument()) this.shim.remove();
    }
    this.container.setStyles({
      left: '-999em',
      top: '-999em'
    }).remove();
    this.visible = false;
    this.fireEvent('onHide');
    return this;
  },
  
  /**
   * Configures the backdrop.
   */
  configureBackdrop: function() {
    this.backdrop = new Element('div');
    this.backdrop.setStyles({
      position: 'absolute',
      left: 0,
      top: 0,
      width: window.getScrollWidth(),
      height: window.getScrollHeight(),
      backgroundColor: '#000',
      zIndex: this.options.isChild ? '10000' : '9997'
    }).setOpacity(0.6);
  },
  
  /**
   * update backdrop on show
   */
  updateBackdrop: function() {
    this.backdrop.setStyles({
      width: window.getScrollWidth(),
      height: window.getScrollHeight()
    });
  },
  
  /**
   * Adds the opacity backdrop behind this layer.
   */
  enableBackdrop: function() {
    if (this.backdropOn) return;
    if (!this.backdrop) this.configureBackdrop();
    if(window.ie6)
    {
      var container = this.container;
      $$('select').each(function(elem){
        if(!container.hasChild(elem))
        {
          elem.setStyle('visibility', 'hidden');
        }
        else
        {
          elem.setStyle('visibility', 'visible');
        }
      });
    }
    ta.store('overlays.current', this);
    this.updateBackdrop();
    if(this.container.inDocument()) this.backdrop.injectBefore(this.container);
    else this.backdrop.injectBefore(document.body);
    this.backdropOn = true;
    this.container.setStyle('z-index', this.options.isChild? '10002' : '9998');
  },
  
  /**
   * Removes the opacity backdrop from behind this layer.
   */
  disableBackdrop: function() {
    if (!this.backdropOn) return;
    this.backdrop.remove();
    if(ta.retrieve('overlays.current') == this)
    {
      ta.remove('overlays.current');
      if(window.ie6)
      {
        $$('select').setStyle('visibility', 'visible');
      }
    }
    else
    {
      if(window.ie6 && ta.retrieve('overlays.current') && !ta.retrieve('overlays.current').backdropOn)
      {
        $$('select').setStyle('visibility', 'visible');
      }
    }
    this.backdropOn = false;
    this.container.setStyle('z-index', '1001');
  },
  
  /**
   * Request remote content.
   */
  loadRemoteContent: function(uri, bHideSpinner) {
    if(!bHideSpinner)
    {
        new Asset.image(CDNHOST + 'loop.gif', {
          onload: this.position.bind(this),
          'class': 'anim_loop'
        }).injectInside(this.inner);
    }
    new Ajax(ta.util.URL.parse(uri), {
      data: this.options.requestData,
      onSuccess: this.loadRemoteSuccess.bind(this),
      onFailure: this.loadRemoteFailure.bind(this),
      evalScripts: true
    }).request();
    return this;
  },
  
  /**
   * Called when remote content is successfully loaded.
   * @param {String} txt The remote content
   */
  loadRemoteSuccess: function(txt) {
    this.cachedWidth = null;
  	this.inner.setHTML(txt);
    this.position();
    if(typeof behavior != 'undefined' && behavior && behavior.apply)
    {
      behavior.apply(this.inner);
    }
    this.fireEvent('onLoad', this);
    this.cachedWidth = this.container.getSize().size.x;
    return this;
  },
  
  /**
   * Called when remote content fails to load.
   */
  loadRemoteFailure: function() {
    this.inner.setText(JS_Ajax_failed);
  }
});
ta.overlays.Overlay.implement(new Options, new Events);
/**
 * An overlay positioned absolutely in the window.
 * 
 * @option {boolean} [showCloseButton=true] Whether or not to show the close button
 * @option {integer} [xOffset=0] Left position of overlay
 * @option {integer} [yOffset=0] Top position of overlay
 * @option {integer} [toWindow=true] Positions in window if true, on document if false.
 */
ta.overlays.AbsoluteOverlay = ta.overlays.Overlay.extend({
  options: {
    xOffset: 0,
    yOffset: 0,
    toWindow: true
  },
  
  initialize: function(options, elmt) {
    options = $pick(options, {});
    options.showCloseButton = $pick(options.showCloseButton, true);
    this.parent(options, elmt);
    
    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideHandler);
    }
    
    this.show();
  },
  
  /**
   * Positions the container.
   */
  position: function() {
    this.container.setStyles({
      left: this.options.xOffset,
      top: this.options.yOffset + (this.options.toWindow ? window.getScrollTop() : 0)
    });
    this.positionShim();
  }
});
/**
 * An overlay positioned in the center of the window.
 * 
 * @option {boolean} [showCloseButton=true] Whether or not to show the close button
 */
ta.overlays.CenteredOverlay = ta.overlays.Overlay.extend({
  options: {
    delayedPosition: false,
    autoShow: true
  },
  
  initialize: function(options, elmt) {
    options = $pick(options, {});
    options.showCloseButton = $pick(options.showCloseButton, true);
    this.parent(options, elmt);
        
    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideHandler);
    }

    if (this.options.autoShow) this.show();
  },
  
  /**
   * Positions the container.
   */
  position: function() {
    if (this.options.delayedPosition) this._position.delay(10, this);
    else this._position();
  },
  
  _position: function() {
    var c = this.container.getCoordinates();
    this.container.setStyles({
      left: Math.max(5,(window.getWidth() - c.width)) / 2 + window.getScrollLeft(),
      top: Math.max(20, window.getHeight() - c.height) / 2 + window.getScrollTop()
    });
    this.positionShim();
  }
});

ta.overlays.showInLightbox = function(txt, ops){
  return new ta.overlays.CenteredOverlay($merge({backdrop: ta.overlays.BACKDROP_ALWAYS}, ops)).loadRemoteSuccess(txt);
};

ta.overlays.loadInLightbox = function(uri){
  return new ta.overlays.CenteredOverlay({backdrop: ta.overlays.BACKDROP_ALWAYS}).loadRemoteContent(uri);
};

// bug: 38152
// temporary fix to catch any remaining issues, should be removed when all references to "window.lightbox" have been removed.
var lightbox = {
  deactivate: function() {
    var lb = ta.retrieve('overlays.current');
	  if (lb) lb.hide();
  }
};
/** Do not attach show/hide actions. */
ta.overlays.ACTIVATE_NEVER  = 0;
/** Show the flyout when clicking on the source element */
ta.overlays.ACTIVATE_CLICK  = 1;
/** Show the flyout when mousing over the source element */
ta.overlays.ACTIVATE_HOVER  = 2;
/** Show the overlay when the element gains focus. */
ta.overlays.ACTIVATE_FOCUS  = 3;

/** Do not pin the flyout open */
ta.overlays.PINNABLE_NEVER  = 0;
/** Always pin the flyout open. Clicking on document will not close the flyout */
ta.overlays.PINNABLE_ALWAYS = 1;
/** Pin the flyout when clicking on the source element */
ta.overlays.PINNABLE_CLICK  = 2;

/** Only show the backdrop when pinned */
ta.overlays.BACKDROP_PINNED = 2;

/**
 * Basic Flyout layer. A flyout is defined as a layer with an arrow pointing to a source element.
 * @class
 *
 * @option {boolean} [activate=ta.layers.ACTIVATE_NEVER] Should be one of ta.layers.ACTIVATE_*
 * @option {boolean} [pinnable=ta.layers.PINNABLE_NEVER] Should be one of ta.layers.PINNABLE_*
 * @option {boolean} [autoShow=true] Show overlay upon creation.
 */
ta.overlays.RelativeOverlay = ta.overlays.Overlay.extend({
  options: {
    activate: ta.overlays.ACTIVATE_NEVER,
    pinnable: ta.overlays.PINNABLE_NEVER,
    autoShow: true
  },
  
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent(options, elmt);
    
    this.container.addClass('relative');

    var parentOverlay = this.source.getParent('.overlay');
    if (parentOverlay) {
      this.container.setStyle('z-index', parentOverlay.getStyle('z-index')+1);
      this.parentOverlay = parentOverlay.overlay;
    }

    // additional event handlers
    this.pinHandler     = this._pin.bindWithEvent(this);
    this.hideNowHandler = this.hideNow.bindWithEvent(this);
    
    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideNowHandler);
    }
    
    // activate type
    switch (this.options.activate) {
      case ta.overlays.ACTIVATE_CLICK:
        this.source.addEvent('click', this.showHandler);
        break;
    
      case ta.overlays.ACTIVATE_HOVER:
        this.source.addEvent('mouseenter', this.showHandler);
        this.source.addEvent('mouseleave', this.hideHandler);
        this.container.addEvent('mouseenter', this.showHandler);
        this.container.addEvent('mouseleave', this.hideHandler);
        break;
      
      case ta.overlays.ACTIVATE_FOCUS:
        this.source.addEvent('focus', this.showHandler);
        break;
    }
    
    // pinnable type
    switch (this.options.pinnable) {
      case ta.overlays.PINNABLE_CLICK:
        this.source.addEvent('click', this.pinHandler);
        break;
    }
    
    // local contents
    var contents = this.source.getElement('.overlayContents');
    if (contents) {
      this.inner.setHTML(contents.innerHTML);
      contents.remove();
      this.runBehaviorOnFirstShow = true;
    }
    
    // remote contents
    var remote = this.source.getElement('.overlaySrc');
    if (remote) {
      if (remote.getTag() == 'a') {
        this.loadRemoteContent(remote.href);
      }
      else {
        this.loadRemoteContent(remote.getText());
        remote.remove();
      }
    }
    else if (this.source.getTag() == 'a')
    { 
      this.loadRemoteContent(this.source.href);
    }
    
    if (this.options.autoShow) this.show();
  },
  
  /**
   * Adds the container to the DOM and positions it.
   * @param {Event} evnt The event
   */
  show: function(evnt) {
    evnt = evnt || window.event;
    if (evnt) new Event(evnt).stop();
    var vis = this.visible;
    this.parent(evnt);
    
    if (this.options.activate == ta.overlays.ACTIVATE_HOVER && this.hideTimer) {
      $clear(this.hideTimer);
      this.hideTimer = null;
    }
    if (vis) return;
    
    if (this.runBehaviorOnFirstShow){
      this.runBehaviorOnFirstShow = false;
      behavior.apply(this.inner);
      this.cachedWidth = this.container.getSize().size.x;
    }
    
    switch (this.options.activate) {
      case ta.overlays.ACTIVATE_HOVER:
        break;
      
      case ta.overlays.ACTIVATE_CLICK:
      case ta.overlays.ACTIVATE_FOCUS:
        if (this.options.pinnable == ta.overlays.PINNABLE_NEVER) {
          document.addEvent('click', this.hideHandler);
        }
        break;
    }

    return this;
  },
  
  /**
   * Removes the container from the DOM.
   * @param {Event} evnt The event
   */
  hide: function(evnt) {
    evnt = evnt || window.event;
    if (!this.visible) return;
    if (window.flyout) window.flyout.hide();
    if (evnt)
    {
      var e = new Event(evnt);
      var eTarg = $(e.target);
      if (eTarg.getTag() == 'option') return; // event occurred on an open select dropdown
      if (window.ie && eTarg.getTag() == 'select' && this.container.getElements('select').contains(e)) return;
    }
    switch (this.options.activate) {
      case ta.overlays.ACTIVATE_HOVER:
        //CHECK TO SEE IF SRC OR CONTAINER CONTAINS EVNT, IF SO IGNORE, IF NOT: HIDE TIMER ROUTINE
        if (!this.hideTimer) {
          this.hideTimer = this.hide.delay(250, this);
          return;
        }
        else {
          this.hideTimer = null;
        }
        break;
      
      case ta.overlays.ACTIVATE_CLICK:
      case ta.overlays.ACTIVATE_FOCUS:
        if (this.options.pinnable == ta.overlays.PINNABLE_NEVER) {
          document.removeEvent('click', this.hideHandler);
        }
        if (evnt && this.container.contains(evnt)) return;
        break;
    }
    return this.hideNow();
  },
  
  /**
   * hack for mootools 1.1
   * Called from hide to execute hide after some checks
   * @param {Event} evnt The event
   */
  hideNow: function(evnt) {
    if (!this.visible) return this;
    if (this.backdropOn) this.disableBackdrop();
    
    if (this.shim) {
      this.shim.setStyles({
        left: '-999em',
        top: '-999em'
      }).remove();
    }
    this.container.setStyles({
      left: '-999em',
      top: '-999em'
    }).remove();
    this.visible = false;
    if (this.options.pinnable == ta.overlays.PINNABLE_CLICK) this.unpin();
    this.fireEvent('onHide');
    return this;
  },
  
  _pin: function(evnt) {
  	this.pin();
  	this.toggleBackdrop(true);
  },
  
  /**
   * Used when pinnable is set to PIN_CLICK.
   * @param {Event} evnt The event
   */
  pin: function() {
    this.source.removeEvent('mouseenter', this.showHandler);
    this.source.removeEvent('mouseleave', this.hideHandler);
    this.container.removeEvent('mouseenter', this.showHandler);
    this.container.removeEvent('mouseleave', this.hideHandler);
    this.pinned = true;
    return this;
  },
  
  toggleBackdrop: function(enable) {
  	if (this.options.backdrop == ta.overlays.BACKDROP_PINNED) this.enableBackdrop();
  },
  
  /**
   * Unpins this flyout, re-adds the mouse enter/leave handlers.
   */
  unpin: function() {
    this.source.addEvent('mouseenter', this.showHandler);
    this.source.addEvent('mouseleave', this.hideHandler);
    this.container.addEvent('mouseenter', this.showHandler);
    this.container.addEvent('mouseleave', this.hideHandler);
    if (this.options.backdrop == ta.overlays.BACKDROP_PINNED) this.disableBackdrop();
    this.pinned = false;
    return this;
  }
});
/**
 * Left/Right Flyout layer.
 * @class
 * 
 * @option {boolean} onRight True for flyout on the right, false for left.
 */
ta.overlays.RelativeOverlayHorizontal = ta.overlays.RelativeOverlay.extend({

  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    if (!$defined(options.onRight)) {
      if (IS_DEBUG) alert("ERROR: Attempting to create Horizontal Overlay without specifying direction.");
      return;
    }
    this.parent(options, elmt);
  },
  
  /**
   * Positions the container.
   */
  position: function() {
    if (this.options.onRight) this.positionRight();
    else this.positionLeft();
    this.positionShim();
  },

  adjustTop: function(posTop, containerCoords){
    var t = posTop;
    // adjust if inside a scolling element
    if (this.source.hasClass('overflown')){
      var scrollable = this.source.getParent('.scrollable');
      if (scrollable) t = t - scrollable.getSize().scroll.y;
    }
    // adjust if too near bottom edge
    var bottomEdge = window.getScrollTop() + window.getHeight();
    if (t + containerCoords.height >= bottomEdge){
      t = bottomEdge - containerCoords.height - 1;
    }
    return t;
  },
  
  /**
   * Positions the container to the left of the source element.
   */
  positionLeft: function() {
    var s = this.source.getCoordinates();
    var mT = this.container.getStyle('margin-top').toInt();
    var pageSize = $('PAGE').getCoordinates();
    var c = this.container.getCoordinates();
    // too close to left edge of #PAGE?
    if (!this.flipped && s.left - pageSize.left - (this.cachedWidth || c.width) < 0) {
      this.flipped = true;
      this.options.onRight = true;
      this.positionRight();
      return;
    }
    var posTop = this.adjustTop(s.top, c);
    // final position
    this.container.setStyles({
      left: null,
      right: window.getWidth() - s.left,
      top: posTop - mT
    });
  },
  
  /**
   * Positions the container to the right of the source element.
   */
  positionRight: function() {
    var s = this.source.getCoordinates();
    var mT = this.container.getStyle('margin-top').toInt();
    var pageSize = $('PAGE').getCoordinates();
    var c = this.container.getCoordinates();
    // too close to right edge of #PAGE?
    if (!this.flipped && pageSize.right - s.right - (this.cachedWidth || c.width) < 0) {
      this.flipped = true;
      this.options.onRight = false;
      this.positionLeft();
      return;
    }
    var posTop = this.adjustTop(s.top, c);
    // final position
    this.container.setStyles({
      right: null,
      left: s.right,
      top: posTop - mT
    });
  }
});
/**
 * Convenience class for creating an overlay to the right.
 * @class
 */
ta.overlays.RelativeOverlayRight = ta.overlays.RelativeOverlayHorizontal.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent($merge({onRight:true}, options), elmt);
  }
});
/**
 * Convenience class for creating an overlay to the left.
 * @class
 */
ta.overlays.RelativeOverlayLeft = ta.overlays.RelativeOverlayHorizontal.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent($merge({onRight:false}, options), elmt);
  }
});
/**
 * Flyout below the source element.
 * @class
 *
 */
ta.overlays.RelativeOverlayVertical = ta.overlays.RelativeOverlay.extend({

  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    if (!$defined(options.below)) {
      if (IS_DEBUG) alert("ERROR: Attempting to create Vertical Overlay without specifying direction.");
      return;
    }
    this.parent(options, elmt);
  },
  
  /**
   * Positions the container.
   */
  position: function() {
    if (this.options.below) this.positionBelow();
    else this.positionAbove();
    this.positionShim();
  },
  
  /**
   * Positions the container.
   */
  positionBelow: function() {
    var s = this.source.getCoordinates();
    var c = this.container.getCoordinates();
    var mL = this.container.getStyle('margin-left').toInt();
    var bottomEdge = window.getHeight() + window.getScrollTop();
    var posTop = s.bottom;
    // only flip above if flyout will fit
    if (!this.flipped && bottomEdge - c.height - posTop < 0 && s.top - c.height > 0) {
      this.options.below = false;
      this.flipped = true;
      this.positionAbove();
      return;
    }
    this.container.setStyles({
      left: this.adjustEdge(s.left - mL, c.width),
      top: posTop
    });
  },
  
  /**
   * Positions the container.
   */
  positionAbove: function() {
    var s = this.source.getCoordinates();
    var c = this.container.getCoordinates();
    var mL = this.container.getStyle('margin-left').toInt();
    var topEdge = window.getScrollTop();
    var posTop = s.top - c.height;
    if (!this.flipped && posTop - topEdge < 0) {
      this.options.below = true;
      this.flipped = true;
      this.positionBelow();
      return;
    }
    this.container.setStyles({
      left: this.adjustEdge(s.left - mL, c.width),
      top: posTop
    });
  },
  
  /**
   * Adjusts the edge number so that the container is fully within the PAGE bounds.
   * @param edge the left edge
   * @param width the container's width
   * @returns {integer} the adjusted left edge
   */
  adjustEdge: function(edge, width){
    var pageSize = $('PAGE').getCoordinates();
    if (edge + width >= pageSize.right){
      var mR = this.container.getStyle('margin-right').toInt();
      edge = pageSize.right - width - mR - 1;
    }
    return edge;
  },
  
  /**
   * Removes the container from the DOM.
   * @param {Event} evnt The event
   */
  hideNow: function(evnt) {
    this.parent(evnt);
    this.flipped = false;
  }
});
/**
 * Convenience class for creating an overlay below.
 * @class
 */
ta.overlays.RelativeOverlayBelow = ta.overlays.RelativeOverlayVertical.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent($merge({below:true}, options), elmt);
  }
});
/**
 * Convenience class for creating an overlay above.
 * @class
 */
ta.overlays.RelativeOverlayAbove = ta.overlays.RelativeOverlayVertical.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent($merge({below:false}, options), elmt);
  }
});

(function() {
  var _findRemoteURI = function(elmt) {
  	if (elmt.getTag() == 'a') return elmt.href;
    var tmp = elmt.getElement('a');
    if (tmp) return tmp.href;
    tmp = elmt.getElement('.overlaySrc');
    if (tmp) return tmp.getText();
    return null;
  }
  
/**
 * Overlay factory.
 * @class
 */
ta.overlays.Factory = {
  
  /**
   * Creates an overlay in the center of the screen, with a backdrop, loading the content from the
   * href of the element, or the href of the first A child element, or the contents of the
   * overlaySrc element.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relRightRemoteHLB: function(evt, elmt) {
  	elmt = $(elmt);
  	if(elmt.getTag() != 'div')
  	{
  		elmt = elmt.getParent();
  	}
  	var uri = _findRemoteURI(elmt);
  	if (uri == null) return;
    var overlay = new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_HOVER,
      pinnable: ta.overlays.PINNABLE_CLICK,
      backdrop: ta.overlays.BACKDROP_PINNED,
      style: 'commerceOverlay',
      showCloseButton: true
    }, elmt);
  },
  
  relRightRemoteHLB_Commerce: function(evnt, elmt) {
    var e = new Event(evnt || window.event);
    e.preventDefault();
    elmt = $(elmt);
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_CLICK,
      pinnable: ta.overlays.PINNABLE_ALWAYS,
      backdrop: ta.overlays.BACKDROP_ALWAYS,
      style: 'commerceOverlay',
      showCloseButton: true
    }, elmt);
  },
  
  relRightRemoteHLB_CommCopy: function(evnt, elmt) {
    var e = new Event(evnt || window.event);
    e.preventDefault();
    elmt = $(elmt);
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_HOVER,
      pinnable: ta.overlays.PINNABLE_CLICK,
      backdrop: ta.overlays.BACKDROP_PINNED,
      style: 'commerceOverlay',
      showCloseButton: true,
      onLoad: ta.commerce.CROverlay.commCopyUpdate
    }, elmt);
  },
  
  
  relBelowRemoteHLB_Commerce: function(evnt, elmt){
    elmt = $(elmt);
    new Event(evnt || window.event).preventDefault();
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_CLICK,
      pinnable: ta.overlays.PINNABLE_ALWAYS,
      backdrop: ta.overlays.BACKDROP_ALWAYS,
      style: 'commerceOverlay',
      showCloseButton: true
    }, elmt);
  },
  
  relBelowRemoteHLB_CommCopy: function(evnt, elmt){
    elmt = $(elmt);
    new Event(evnt || window.event).preventDefault();
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_CLICK,
      pinnable: ta.overlays.PINNABLE_ALWAYS,
      backdrop: ta.overlays.BACKDROP_ALWAYS,
      style: 'commerceOverlay',
      showCloseButton: true,
      onLoad: ta.commerce.CROverlay.commCopyUpdate
    }, elmt);
  },
  
  /**
   * Creates a default overlay below the element. Should be called on click. Note that this
   * handler stops the event.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relBelow: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK
    }, elmt);
  },
  
  /**
   * Creates a default overlay above the element. Should be called on click. Note that this
   * handler stops the event.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relAbove: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayAbove({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK
    }, elmt);
  },
  
  /**
   * Creates a default overlay to the right. Should be called onclick. Note that this
   * handler stops the event.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relRight: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayRight({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK
    }, elmt);
  },
  
  /**
   * Creates a default overlay to the left. Should be called onclick. Note that this
   * handler stops the event.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relLeft: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayLeft({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK
    }, elmt);
  },
  
  /**
   * Creates a default overlay below the element. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relBelowH: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_HOVER
    }, elmt);
  },
  
  /**
   * Creates a mediaBox overlay below the element. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relBelowH_Media: function(evnt, elmt) {
    evnt.stop();
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_HOVER,
      style: 'typeO mediaBox'
    }, elmt);
  },
  
  /**
   * Creates a default overlay above the element. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relAboveH: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayAbove({
      activate: ta.overlays.ACTIVATE_HOVER
    }, elmt);
  },
  
  /**
   * Creates a default overlay to the right. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relRightH: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_HOVER
    }, elmt);
  },
  
  /**
   * Creates a default overlay to the left. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relLeftH: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayLeft({
      activate: ta.overlays.ACTIVATE_HOVER
    }, elmt);
  },
  
  /**
   * Creates a typeB style overlay to the right. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  typeB_relRightH: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_HOVER,
      style: 'commerce'
    }, elmt);
  },
  
  /**
   * Creates a typeB style overlay to the right. Should be called onmouseover. Converts to pinned
   * with backdrop on click.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  typeB_relRightHLB: function(evnt, elmt) {
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayRight({
      activate: ta.overlays.ACTIVATE_HOVER,
      pinnable: ta.overlays.PINNABLE_CLICK,
      backdrop: ta.overlays.BACKDROP_PINNED,
      style: 'commerce',
      showCloseButton: true
    }, elmt);
  },
  
  /**
   * Creates an overlay in the center of the screen, with a backdrop, loading the content from the
   * href of the element, or the href of the first A child element, or the contents of the
   * overlaySrc element.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  centeredRemoteLB: function(evnt, elmt) {
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    evnt.preventDefault();
    var overlay = new ta.overlays.CenteredOverlay({
      backdrop: ta.overlays.BACKDROP_ALWAYS
    });
    overlay.loadRemoteContent(uri);
  },
  
  /**
   * Creates an overlay in the top left corner of the screen, loading the content from the
   * href of the element, or the href of the first A child element, or the contents of the
   * overlaySrc element.
   */
  absRemote: function(evnt, elmt) {
    var uri = _findRemoteURI(elmt);
    if (uri == null) return;
    evnt.preventDefault();
    var overlay = new ta.overlays.AbsoluteOverlay();
    overlay.loadRemoteContent(uri);
  }
}
})();
/**
 * An overlay to show the calendar widget.
 * @class
 *
 * @option {boolean} [showCloseButton=true] show the close button
 * @option {integer} [activate=ta.overlays.ACTIVATE_FOCUS] method of overlay activation
 * @option {String} [style=typeO cal] overlay style class
 * @option {boolean} [autoShow=false] false to prevent overlay from being shown before calendar is ready
 * @option {boolean} [autoShowCal=false] show calendar on creation
 * @option {Object} [calendarOptions] hash of options to use when creating the ta.widgets.Calendar
 */
ta.overlays.CalendarOverlay = ta.overlays.RelativeOverlayBelow.extend({
  options: {
    showCloseButton: true,
    activate: ta.overlays.ACTIVATE_FOCUS,
    style: 'typeO ocal',
    autoShow: false,
    autoShowCal: false,
    calendarOptions: null
  },

  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(container, options){
    this.input = container.getElement('input[type=text]');
    this.parent(options, this.input);

    this.input.overlay = this;
    this.dayField = container.getElement('input.day');
    this.monthField = container.getElement('input.month');
    
    this.source.addEvent('click', this.showHandler);
        
    this.icn = container.getElement('span.icn');
    if (this.icn){
      this.icn.onclick = "";
      this.icn.addEvent('click', this.showHandler);
    }
    
    this.calendar = new ta.widgets.Calendar($merge({
      dualCalendar: true,
      choiceLink: 'ta.overlays.calendarSelect',
      prevAction: 'ta.overlays.calendarPrev',
      nextAction: 'ta.overlays.calendarNext',
      onSelect: this.select.bind(this)
    }, this.options.calendarOptions));
    this.inner.adopt(this.calendar.container);
    
    this.parentOverlay = container.getParent('.overlay');
    if (this.parentOverlay){
      this.parentOverlay = this.parentOverlay.overlay;
      this.options.isChild = true;
    }
    
    if (this.options.autoShowCal) this.show();
  },
  
  /**
   * Removes any links between js and DOM to prevent memory leaks.
   */
  destroy: function(){
    this.parent();
    this.input.overlay = null;
  },
  
  /**
   * Adds the container to the DOM and positions it.
   * @param {Event} evnt The event
   */
  show: function(e){
    var vis = this.visible;
    this.parent(e);
    if (vis) return;
    // pin the parent overlay if it is pinnable and isn't already
    if (this.parentOverlay && this.parentOverlay.pin && this.parentOverlay.options.pinnable == ta.overlays.PINNABLE_CLICK){
      this.tempPin = !this.parentOverlay.pinned;
      if (this.tempPin) this.parentOverlay.pin();
    }
    this.loadFields();
    this.calendar.update();
    this.calendar.reposition();
    return this;
  },
  
  hide: function(e){
    if (!this.visible) return;
    this.parent(e);
    // unpin the parent overlay if it was pinned because this child was shown
    if (this.parentOverlay && this.tempPin){
      this.tempPin = false;
      this.parentOverlay.unpin();
    }
  },
  
  hideNow: function(e){
    if (!this.visible) return this;
    this.parent(e);
    // unpin the parent overlay if it was pinned because this child was shown
    if (this.parentOverlay && this.tempPin){
      this.tempPin = false;
      this.parentOverlay.unpin();
    }
  },
  
  /**
   * Called when the user selects a date in a calendar.
   * @param {ta.widgets.Calendar} calendar calendar the user selected a date in
   * @param {Event} evnt the event
   */
  select: function(calendar, evnt){
    new Event(evnt).preventDefault();
    this.updateFields();
    this.hide();
    this.fireEvent('onSelect', this);
  },
  
  /**
   * Sets the selected date from the values in the fields.
   */
  loadFields: function(){
    var d = parseInt(this.dayField.value.replace(/^0/,''));
    var m = this.monthField.value.split(/\//);
    if (m.length != 2) return;
    var y = parseInt(m[1]);
    m = parseInt(m[0].replace(/^0/,''));
    this.calendar.selectedDate = new Date(y,m-1,d);
    return this;
  },
  
  /**
   * Updates the fields with the new selected date.
   */
  updateFields: function(){
    if (!this.calendar.selectedDate) this.loadFields();
    if (!this.calendar.selectedDate) return;
    var m = this.calendar.selectedDate.getMonth() + 1; if (m < 10) m = "0" + m;
    this.dayField.value = this.calendar.selectedDate.getDate();
    this.monthField.value = m + "/" + this.calendar.selectedDate.getFullYear();
    this.input.value = formatDate(this.calendar.selectedDate.getDate(), this.calendar.selectedDate.getMonth(), this.calendar.selectedDate.getFullYear());
  },
  
  /**
   * Clears the input field, showing instead the date format.
   */
  clearFields: function(){
    this.input.value = JS_DateFormat;
  },
  
  /**
   * Verifies the range and format of the fields backing this calendar.
   * @returns {String} error message, or null if no error
   */
  validate: function(){
    var r = this.input.value.match(DATE_FORMAT.pattern);
    if (r) {
      var nd = parseInt(r[DATE_FORMAT.date].replace(/^0/,''));
      var nm = parseInt(r[DATE_FORMAT.month].replace(/^0/,''));
      var ny = parseInt(r[DATE_FORMAT.year].replace(/^0/,''));
      //if (d != nd || m != nm || y != ny) {
      if (nd > 0 && nd < 32 && nm > 0 && nm < 13) {
        if (nd < 10) nd = '0' + nd;
        if (nm < 10) nm = '0' + nm;
        if (ny < 100) ny += 2000;
        this.dayField.value = nd;
        this.monthField.value = nm + "/" + ny;
        this.loadFields();
        if (!this.calendar.isValid()) return sInvalidDates; // date out of range
      }
      else { return sInvalidDates; } // number out of range
    }
    else { return sInvalidDates; } // invalid format
    return null;
  }
});

ta.overlays.createCalendar = function(evnt, elmt){
  new Event(evnt).preventDefault();
  elmt.onfocus = "";
  new ta.overlays.CalendarOverlay($(elmt).getParent('.cal'), {autoShowCal: true});
}

// These functions are for performance. They allow us to construct the html for the calendar using
// strings and then hook back into the calendar instance to call the functions as normal.

/**
 * Click handler for dates in the calendar.
 * @param {Event} evnt the event
 * @param {integer} y the year
 * @param {integer} m the month (0 indexed)
 * @param {integer} d the day of the month
 * @param {Element} elmt element the user clicked on
 */
ta.overlays.calendarSelect = function(evnt, y, m, d, elmt){
  $(elmt).getParent('.overlay').overlay.calendar.select(evnt, new Date(y, m, d));
}

/**
 * Click handler for the previous month button.
 * @param {Event} evnt the event
 * @param {Element} elmt the button
 */
ta.overlays.calendarPrev = function(evnt, elmt){
  $(elmt).getParent('.overlay').overlay.calendar.prev();
}

/**
 * Click handler for the next month button.
 * @param {Event} evnt the event
 * @param {Element} elmt the button
 */
ta.overlays.calendarNext = function(evnt, elmt){
  $(elmt).getParent('.overlay').overlay.calendar.next();
}
/**
 * Creates a pair of calendars where the date in the first cannot come after the date in the second,
 * or vice-versa.
 * @class
 *
 * @option {boolean} [validate=false] validate calendars before form submission
 * @option {boolean} [allowSameDay=false] allow same day to be selected on both calendars
 * @option {Object} [calendarOptions] hash of options to use when creating the ta.widgets.Calendar
 */
ta.overlays.PairedCalendar = new Class({
  options: {
    validate: false,
    allowSameDay: false,
    calendarOptions: null
  },

  /**
   * @param {Element} elmt any child element of .dualCal
   * @param {Object} [options] options, see below
   */
  initialize: function(elmt, options){
    this.setOptions(options);
    elmt = $(elmt);
    this.form = elmt.getTag() == 'form' ? elmt : elmt.getParent('form');
    var overlayOptions = $merge({onSelect: this.adjustRange.bind(this)}, this.options.overlayOptions);
    if (this.options.calendarOptions) overlayOptions.calendarOptions = this.options.calendarOptions;
    this.form.getElements('.cal').each(function(container){
      var cal = new ta.overlays.CalendarOverlay(container, overlayOptions);
      cal.input.onfocus = "";
      if (container.hasClass('first')) this.before = cal;
      else this.after = cal;
      if (elmt == cal.input || elmt == cal.icn) cal.show();
    }, this);
    
    if (this.form){
      this.form.calendar = this;

      // add submit event to the form to validate dates
      if (this.options.validate){
        this.form.addEvent('submit', this.validate.bindWithEvent(this));
        this.errorDiv = this.form.getElement('.error_msg');
      }
    }
  },

  /**
   * Finds the corresponding overlay given it's calendar.
   * @param {ta.widgets.Calendar} calendar the calendar
   * @returns {ta.overlays.CalendarOverlay} the overlay
   */
  getOverlayForCalendar: function(calendar){
    if (this.before.calendar == calendar) return this.before;
    if (this.after.calendar == calendar) return this.after;
    return null;
  },
  
  /**
   * Adjusts the other calendar of the pair if the range is invalid.
   */
  adjustRange: function(overlay){
    this.requireFullLoad();
    if (this.form.searchAll){
      if (this.form.searchAll.type == 'checkbox') this.form.searchAll.checked = false;
      else if (this.form.searchAll.type == 'hidden') this.form.searchAll.value = 'false';
    }
    // if after is set to first date and same day is not enabled, set before to first date and after to one day later
    if (!this.sameDayEnabled() && this.after.calendar.selectedDate && this.after.calendar.selectedDate.getTime() == this.after.calendar.options.firstDate.getTime()){
      this.before.calendar.selectedDate = new Date(this.before.calendar.options.firstDate);
      var d = new Date(this.after.calendar.options.firstDate);
      d.setDate(d.getDate()+1);
      this.after.calendar.selectedDate = d;
      this.before.updateFields();
      this.after.updateFields();
    }
    else {
      if (overlay == this.before){
        if (!this.isBeforeValid()){
          var d = new Date(this.before.calendar.selectedDate);
          d.setDate(d.getDate() + 1);
          this.after.calendar.selectedDate = d;
        }
        this.after.updateFields();
      }
      else if (overlay == this.after){
        if (!this.isAfterValid()){
          var d = new Date(this.after.calendar.selectedDate);
          d.setDate(d.getDate() - 1);
          this.before.calendar.selectedDate = d;
        }
        this.before.updateFields();
      }
    }
    this.fireEvent('onUpdate', this);
    var callbacks = ta.retrieve('calendar.onUpdateCallback');
    if (callbacks){
      for (var i=0;i<callbacks.length;i++){
        if (window[callbacks[i]]) window[callbacks[i]](this, overlay);
      }
    }
  },
  
  /**
   * Checks if this form supports the one-way option and that it is enabled.
   * @returns {boolean} true if the one-way option is enabled
   */
  isOneWay: function(){
    return this.form.oneWay && this.form.oneWay.checked;
  },
  
  /**
   * Checks if before and after calendars can have the same day selected.
   * @returns {boolean} true if same day option is enabled
   */
  sameDayEnabled: function(){
    return this.options.allowSameDay || this.isOneWay();
  },
  
  /**
   * Checks if the form supports the any-date option and that it is enabled.
   * @returns {boolean} true if the any-date option is enabled
   */
  searchAllEnabled: function(){
    return this.form.searchAll && (this.form.searchAll.checked || (this.form.searchAll.type == 'hidden' && this.form.searchAll.value == 'true'));
  },

  /**
   * Updates the text field of both calendars to show the selected date.
   */
  updateFields: function(){
    this.before.updateFields();
    this.after.updateFields();
  },

  /**
   * Sets both text fields to show the date format string instead of a selected date.
   */
  clearFields: function(){
    this.before.clearFields();
    this.after.clearFields();
  },
  
  /**
   * Ensures that both calendars have their selected date set.
   */
  requireFullLoad: function(){
    if (!this.before.calendar.selectedDate) this.before.loadFields();
    if (!this.after.calendar.selectedDate) this.after.loadFields();
  },
  
  /**
   * Checks the validity of the before calendar.
   * @returns {boolean} true if the before date is valid
   */
  isBeforeValid: function(){
    this.requireFullLoad();
    if (!this.after.calendar.selectedDate) return true; // assume invalid dates are valid
    return (this.before.calendar.selectedDate < this.after.calendar.selectedDate) ||
      (this.sameDayEnabled() && this.before.calendar.selectedDate.getTime() <= this.after.calendar.selectedDate.getTime());
  },
  
  /**
   * Checks the validity of the after calendar.
   * @returns {boolean} true if the after date is valid
   */
  isAfterValid: function(){
    this.requireFullLoad();
    if (!this.before.calendar.selectedDate) return true; // assume invalid dates are valid
    return (this.after.calendar.selectedDate > this.before.calendar.selectedDate) ||
      (this.sameDayEnabled() && this.after.calendar.selectedDate.getTime() >= this.before.calendar.selectedDate.getTime());
  },
  
  /**
   * Validate that the calendars are correct. This is where user input is checked.
   */
  validate: function(e){
    if (this.searchAllEnabled()) return true;  
    var eb = this.before.validate();
    var ea = this.after.validate();
    var valid = true;
  
    if (this.isOneWay()) {
      if (eb != null) {
        valid = false;
        this.setError(eb);
      }
    }
    else if (eb != null || ea != null) {
      valid = false;
      this.setError(eb || ea);
    }
    else if (!this.isAfterValid()) {
      valid = false;
      this.setError(sInvalidDates);
    }
    if (!valid && e) new Event(e).stop();
    return valid;
  },
  
  /**
   * Sets and shows the error message.
   * @param {String} msg error message to show
   */
  setError: function(msg){
    if (!this.errorDiv) return;
    this.errorDiv.setContent(msg).show();
  },
  
  /**
   * Toggles the search all option
   */
  toggleSearchAll: function(){
    if (this.searchAllEnabled()){
      this.before.clearFields();
      this.after.clearFields();
    }
    else {
      this.requireFullLoad();
      this.before.updateFields();
      this.after.updateFields();
    }
  }
});
ta.overlays.PairedCalendar.implement(new Options, new Events);

/**
 * Factory method for creating calendars. Should be called onclick.
 */
ta.overlays.createSimplePairedCalendar = function(evnt, elmt){
  new Event(evnt).stop();
  new ta.overlays.PairedCalendar(elmt);
}

/**
 * Factory method for creating calendars. Should be called onclick.
 */
ta.overlays.createPairedCalendar = function(evnt, elmt, options){
  if(!options) {
	  options = {validate: true};
  }
  new Event(evnt).preventDefault();
  new ta.overlays.PairedCalendar(elmt, options);
}

/**
 * create calendars and switch to display dates
 */
ta.overlays.createDatedPairedCalendar = function(evnt, elmt){
  new Event(evnt).preventDefault();
  elmt = $(elmt);
  var form = elmt.getParent('form');
  new ta.overlays.PairedCalendar(elmt, {validate:true, overlayOptions:{onShow: function(){
    ta.overlays.toggleOffSearchAll(form);
  }}});
  //needs an extra call for first time
  ta.overlays.toggleOffSearchAll(form);
}

ta.overlays.toggleOffSearchAll = function(form){
  if(form.calendar && form.searchAll){
    if (form.searchAll.type == 'checkbox') form.searchAll.checked = false;
    else if (form.searchAll.type == 'hidden') form.searchAll.value = 'false';
    form.calendar.toggleSearchAll();
  }
}

/**
 * Click handler for search all checkbox.
 * @param {Event} evnt the event
 * @param {Element} elmt the checkbox
 */
ta.overlays.toggleSearchAll = function(evnt, elmt){
  elmt = $(elmt);
  elmt.onclick = "";
  var form = elmt.getParent('form');
  if (!form.calendar) new ta.overlays.PairedCalendar(elmt, {validate:true});
  form.calendar.toggleSearchAll(elmt);
  elmt.addEvent('click', form.calendar.toggleSearchAll.bind(form.calendar));
}
/**
 * A permanent overlay positioned absolutely in the window.
 */
ta.overlays.PermanentOverlay = ta.overlays.AbsoluteOverlay.extend({
});
/**
 * Observer - Observe formelements for changes
 *
 * @version		1.0rc1
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
Function.extend({
  // same as delay, but FF 1.0 has issues with the name delay
	invokeLater: function(delay, bind, args){
		return this.create({'delay': delay, 'bind': bind, 'arguments': args})();
	}
});

var Observer = new Class({

	options: {
		periodical: false,
		delay: 1000
	},

	initialize: function(el, onFired, options){
		this.setOptions(options);
		this.addEvent('onFired', onFired);
		this.element = $(el);
		this.listener = this.fired.bind(this);
		this.value = this.element.getValue();
		if (this.options.periodical) this.timer = this.listener.periodical(this.options.periodical);
		else this.element.addEvent('keyup', this.listener);
	},

	fired: function() {
		var value = this.element.getValue();
		if (this.value == value) return;
		this.clear();
		this.value = value;
		this.timeout = this.fireEvent.invokeLater(this.options.delay, this, ['onFired', [value]]);
	},

	clear: function() {
		$clear(this.timeout);
		return this;
	}
});

Observer.implement(new Options);
Observer.implement(new Events);
/**
 * Autocompleter
 *
 * @version		1.0rc4
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var Autocompleter = {};

Autocompleter.Base = new Class({

	options: {
		minLength: 1,
		useSelection: true,
		markQuery: true,
		inheritWidth: true,
		maxChoices: 10,
		injectChoice: null,
		onSelect: Class.empty,
		onShow: Class.empty,
		onHide: Class.empty,
		customTarget: null,
		className: 'autocompleter-choices',
		zIndex: 42,
		observerOptions: {},
		fxOptions: {},
		overflown: [],
		selectOnBlur: false
	},

	initialize: function(el, options) {
		this.setOptions(options);
		this.element = $(el);
		this.build();
		this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
			delay: 100
		}, this.options.observerOptions));
		this.value = this.observer.value;
		this.queryValue = null;
	},

	/**
	 * build - Initialize DOM
	 *
	 * Builds the html structure for choices and appends the events to the element.
	 * Override this function to modify the html generation.
	 */
	build: function() {
		if ($(this.options.customTarget)) this.choices = this.options.customTarget;
		else {
			this.choices = new Element('ul', {
				'class': this.options.className,
				styles: {zIndex: this.options.zIndex}
			}).injectInside(document.body);
			this.fix = new OverlayFix(this.choices);
		}
		this.fx = this.choices.effect('opacity', $merge({
			wait: false,
			duration: 200
		}, this.options.fxOptions))
			.addEvent('onStart', function() {
				if (this.fx.now) return;
				this.choices.setStyle('display', '');
				this.fix.show();
			}.bind(this))
			.addEvent('onComplete', function() {
				if (this.fx.now) return;
				this.choices.setStyle('display', 'none');
				this.fix.hide();
			}.bind(this)).set(0)
      .addEvent('onShow', function() {
				this.fix.show();
      }.bind(this));
		this.element.setProperty('autocomplete', 'off')
			.addEvent(window.ie6 ? 'keydown' : 'keypress', this.onCommand.bindWithEvent(this))
			.addEvent('mousedown', this.onCommand.bindWithEvent(this, [true]))
			.addEvent('focus', this.toggleFocus.bind(this, [true]))
			.addEvent('blur', this.toggleFocus.bind(this, [false]))
			.addEvent('trash', this.destroy.bind(this));
	},

	destroy: function() {
		this.choices.remove();
	},

	toggleFocus: function(state) {
		if (!state && this.focussed && this.options.selectOnBlur && this.selected && this.visible) {
			// losing focus, accept current selection
			this.choiceSelect(this.selected);
		}
		this.focussed = state;
		if (!state) this.hideChoices();
	},

	onCommand: function(e, mouse) {
		if (mouse && this.focussed) this.prefetch();
		if (e.key && !e.shift) switch (e.key) {
			case 'enter':
				if (this.selected && this.visible) {
					this.choiceSelect(this.selected);
					e.stop();
				} return;
			case 'up': case 'down':
				if (this.observer.value != (this.value || this.queryValue)) this.prefetch();
				else if (this.queryValue === null) break;
				else if (!this.visible) this.showChoices();
				else {
					this.choiceOver((e.key == 'up')
						? this.selected.getPrevious() || this.choices.getLast()
						: this.selected.getNext() || this.choices.getFirst() );
					this.setSelection();
				}
				e.stop(); return;
			case 'esc': this.hideChoices(); return;
		}
		this.value = false;
	},

	setSelection: function() {
		if (!this.options.useSelection) return;
		var startLength = this.queryValue.length;
		if (this.element.value.indexOf(this.queryValue) != 0) return;
		var insert = this.selected.inputValue.substr(startLength);
		if (document.getSelection) {
			this.element.value = this.queryValue + insert;
			this.element.selectionStart = startLength;
			this.element.selectionEnd = this.element.value.length;
		} else if (document.selection) {
			var sel = document.selection.createRange();
			sel.text = insert;
			sel.move("character", - insert.length);
			sel.findText(insert);
			sel.select();
		}
		this.value = this.observer.value = this.element.value;
	},

	hideChoices: function() {
		if (!this.visible) return;
		this.visible = this.value = false;
		this.observer.clear();
		this.fx.start(0);
		this.fireEvent('onHide', [this.element, this.choices]);
	},

	showChoices: function() {
		if (this.visible || !this.choices.getFirst()) return;
		this.visible = true;
		var pos = this.element.getCoordinates(this.options.overflown);
		this.choices.setStyles({
			left: pos.left,
			top: pos.bottom
		});
		if (this.options.inheritWidth) this.choices.setStyle('width', pos.width);
		this.fx.start(1);
		this.choiceOver(this.choices.getFirst());
		this.fireEvent('onShow', [this.element, this.choices]);
	},

	prefetch: function() {
		if (this.element.value.length < this.options.minLength) this.hideChoices();
		else if (this.element.value == this.queryValue) this.showChoices();
		else this.query();
	},

	updateChoices: function(choices) {
		this.choices.empty();
		this.selected = null;
		if (!choices || !choices.length) return;
		if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
		choices.each(this.options.injectChoice || function(choice, i){
			var el = new Element('li').setHTML(this.markQueryValue(choice));
			el.inputValue = choice;
			this.addChoiceEvents(el).injectInside(this.choices);
		}, this);
		this.showChoices();
	},

	choiceOver: function(el) {
		if (this.selected) this.selected.removeClass('autocompleter-selected');
		this.selected = el.addClass('autocompleter-selected');
	},

	choiceSelect: function(el) {
		this.observer.value = this.element.value = el.inputValue;
		this.hideChoices();
		this.fireEvent('onSelect', [this.element], 20);
	},

	/**
	 * markQueryValue
	 *
	 * Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
	 * Call this i.e. from your custom parseChoices, same for addChoiceEvents
	 *
	 * @param		{String} Text
	 * @return		{String} Text
	 */
	markQueryValue: function(txt) {
		return (this.options.markQuery && this.queryValue) ? txt.replace(new RegExp('(' + this.queryValue.escapeRegExp() + ')', 'i'), '<span class="autocompleter-queried">$1</span>') : txt;
	},

	/**
	 * addChoiceEvents
	 *
	 * Appends the needed event handlers for a choice-entry to the given element.
	 *
	 * @param		{Element} Choice entry
	 * @return		{Element} Choice entry
	 */
	addChoiceEvents: function(el) {
		return el.addEvents({
			mouseover: this.choiceOver.bind(this, [el]),
			mousedown: this.choiceSelect.bind(this, [el])
		});
	}
});

Autocompleter.Base.implement(new Events);
Autocompleter.Base.implement(new Options);

Autocompleter.Local = Autocompleter.Base.extend({

	options: {
		minLength: 0,
		filterTokens : null
	},

	initialize: function(el, tokens, options) {
		this.parent(el, options);
		this.tokens = tokens;
		if (this.options.filterTokens) this.filterTokens = this.options.filterTokens.bind(this);
	},

	query: function() {
		this.hideChoices();
		this.queryValue = this.element.value;
		this.updateChoices(this.filterTokens());
	},

	filterTokens: function(token) {
		var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
		return this.tokens.filter(function(token) {
			return regex.test(token);
		});
	}

});

Autocompleter.Ajax = {};

Autocompleter.Ajax.Base = Autocompleter.Base.extend({

	options: {
		postVar: 'value',
		postData: {},
		ajaxOptions: {},
		onRequest: Class.empty,
		onComplete: Class.empty
	},

	initialize: function(el, url, options) {
		this.parent(el, options);
		this.ajax = new Ajax(url, $merge({
			autoCancel: true
		}, this.options.ajaxOptions));
		this.ajax.addEvent('onComplete', this.queryResponse.bind(this));
		this.ajax.addEvent('onFailure', this.queryResponse.bind(this, [false]));
	},

	query: function(){
		var data = $extend({}, this.options.postData);
		data[this.options.postVar] = this.element.value;
		this.fireEvent('onRequest', [this.element, this.ajax]);
		this.ajax.request(data);
	},

	/**
	 * queryResponse - abstract
	 *
	 * Inherated classes have to extend this function and use this.parent(resp)
	 *
	 * @param		{String} Response
	 */
	queryResponse: function(resp) {
		this.value = this.queryValue = this.element.value;
		this.selected = false;
		this.hideChoices();
		this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
	}

});

Autocompleter.Ajax.Json = Autocompleter.Ajax.Base.extend({

	queryResponse: function(resp) {
		this.parent(resp);
		var choices = Json.evaluate(resp || false);
		if (!choices || !choices.length) return;
		this.updateChoices(choices);
	}

});

Autocompleter.Ajax.Xhtml = Autocompleter.Ajax.Base.extend({

	options: {
		parseChoices: null
	},

	queryResponse: function(resp) {
		this.parent(resp);
		if (!resp) return;
		this.choices.setHTML(resp).getChildren().each(this.options.parseChoices || this.parseChoices, this);
		this.showChoices();
	},

	parseChoices: function(el) {
		var value = el.innerHTML;
		el.inputValue = value;
		el.setHTML(this.markQueryValue(value));
	}

});


var OverlayFix = new Class({

	initialize: function(el) {
		this.element = $(el);
		if (window.ie6){
			this.element.addEvent('trash', this.destroy.bind(this));
			this.fix = new Element('iframe', {
				properties: {
					frameborder: '0',
					scrolling: 'no',
					src: 'javascript:false;'
				},
				styles: {
					position: 'absolute',
					border: 'none',
					display: 'none',
					filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'
				}
			}).injectAfter(this.element);
		}
	},

	show: function() {
		if (this.fix) this.fix.setStyles($extend(
			this.element.getCoordinates(), {
				display: '',
				zIndex: (this.element.getStyle('zIndex') || 1) - 1
			}));
		return this;
	},

	hide: function() {
		if (this.fix) this.fix.setStyle('display', 'none');
		return this;
	},

	destroy: function() {
		this.fix.remove();
	}

});
/*
Script: extensions.js
  Contains <StringBuffer>, <Table>, <Autocompleter.Ajax.Json2>, <Autocompleter.Ajax.Flights>, <TabSet>

License:
  not free for public use
*/

/*
Class: StringBuffer
  Small implementation of a string buffer.
*/
var StringBuffer = new Class({
  initialize: function(str) {
    this.strings = new Array();
    if (str) this.append(str);
  },

  append: function(str) {
    this.strings.push(str);
    return this;
  },

  toString: function() {
    return this.strings.join('');
  },

  isEmpty: function () {
    return this.strings.length == 0;
  }
});

Cookie.extend({
	set: function(key, value, options){
    Cookie.setRaw(key, encodeURIComponent(value), options);
	},

  setRaw: function(key, value, options){
    options = $merge(this.options, options);
    if (options.domain) value += '; domain=' + options.domain;
    if (options.path) value += '; path=' + options.path;
    if (options.duration){
      var date = new Date();
      date.setTime(date.getTime() + options.duration * 24 * 60 * 60 * 1000);
      value += '; expires=' + date.toGMTString();
    }
    if (options.time){
      var date = new Date();
      date.setTime(date.getTime() + options.time * 1000);
      value += '; expires=' + date.toGMTString();
    }
    if (options.secure) value += '; secure';
    document.cookie = key + '=' + value;
    return $extend(options, {'key': key, 'value': value});
  },

	exist: function(key){
		return document.cookie.match('(?:^|;)\\s*' + key.escapeRegExp() + '=([^;]*)') != null;
	},

    // Like Cookie.get, but doesn't try to decodeURIComponent (fails on some of our cookies)
    getRaw: function(key) {
        var val = document.cookie.match('(?:^|;)\\s*' + key.escapeRegExp() + '=([^;]*)');
        return val ? val[1] : false;
    }
});

Array.extend({
  toQueryString: function() {
    var data = this.map(function(item, idx) {
      switch($type(item)){
        case 'element': return $(item).toQueryString(); break;
        case 'object': return Object.toQueryString(item);
      }
		});
		return data.join('&');
  }
});

Ajax.prototype._initialize = Ajax.prototype.initialize;
Ajax.prototype.initialize = function(url, options){
  // force URLs to be absolute to fix issue with BASE tag on geo-hostnames
  if (!/^http/.test(url)) {
    url = window.location.protocol + '//' + window.location.host + url;
  }
  else if (/^(\w+):\/\/([\w.\-]+)\/(.*)/.test(url) &&  RegExp.$2 != window.location.host) {
    url = RegExp.$1 + '://' + window.location.host + '/' + RegExp.$3;
  }
  // support data as an array
  if (options && options.data && options.data.constructor && options.data.constructor == Array) {
    options.data = options.data.toQueryString();
  }
  
  // continue with normal initialization
  return this._initialize(url, options);
}

Element.extend({
  getParents: function(selector){
    return $$(selector || '').filter(function(el){
      return (el.hasChild(this));}, this).reverse();
  },

  getParent: function(selector){
    if (!selector) return $(this.parentNode);
    return this.getParents(selector)[0] || null;
  },
  
  /** 
   * this hack function is needed for searching elements
   * that have been removed from the dom. It searches by
   * iterating up the parentNode, instead of using
   * the global $$ function which references the dom
  */
  findParentByClass: function(myClass){
    if (!this.parentNode) return null;
    var pNode = $(this.parentNode);
    if (pNode == document) return null;
    if (pNode.hasClass(myClass)) return pNode;
    return pNode.findParentByClass(myClass);
  },  

  getLastElement: function(selector){
    return $(this.getElements(selector, true).reverse()[0] || false);
  },

  centerOnScreen: function(){
    var s = this.getCoordinates();
    this.style.left = Math.round((Window.getWidth() - s.width) / 2) + Window.getScrollLeft() + 'px';
    this.style.top  = Math.round((Window.getHeight() - s.height) / 2) + Window.getScrollTop() + 'px';
  },

  positionOnScreen: function(left, top){
    this.style.left = Window.getScrollLeft() + left + 'px';
    this.style.top  = Window.getScrollTop() + top + 'px';
  },

  contains: function(evnt){
    if (!evnt || !evnt.page) return false;
    evnt = new Event(evnt);
    var s = this.getCoordinates();
    return (evnt.page.y >= s.top &&
            evnt.page.y <= s.bottom &&
            evnt.page.x >= s.left &&
            evnt.page.x <= s.right);
  },

  overlaps: function(elmt){
    var c = this.getCoordinates();
    var e = elmt.getCoordinates();
    var inH = (e.left > c.left && e.left < c.right) || (e.right > c.left && e.right < c.right);
    var top = e.top > c.top && e.top < c.bottom;
    var btm = e.bottom > c.top && e.bottom < c.bottom;
    return (top && inH) || (btm && inH);
  },

  // linkify caries the class from the element being replaced forward to the new 'a' tag
  linkify: function(){
    var a = this.replaceWith(new Element('a', {'class': this.className, href: 'javascript:;'})).setHTML(this.innerHTML);
    _processLink(a);
    return a;
  },

  setContent: function(content) {
    if (content instanceof Array) content.each( function(v) { this.adopt($(v)); }, this );
    // IDs are case insensitive in IE
    // don't get an element by id, just check if its already an element
    else if ($type(content) == 'element') this.adopt($(content));
    else this.setHTML(content);
    return this;
  },

  hidden: function() {
    return this.getStyle('display') == 'none';
  },

  show: function() {
    return this.setStyle('display', 'block');
  },

  hide: function() {
    return this.setStyle('display', 'none');
  },
  
  toggle: function() {
    if (this.hidden()) this.show();
    else this.hide();
  },

  inDocument: function() {
    return this.parentNode != null && this.parentNode.nodeType != 11;
  }
});

/*
Class: Table

Options:
  properties - (object) properties to set on the table
  rows - (array) table contents
*/
// TODO should this extend element with overrides so it can be injected and so forth?
var Table = new Class({
  options: {
    properties: {
      cellpadding: 0,
      cellspacing: 0,
      border: 0
    },
    rows: []
  },

  initialize: function(options) {
    this.setOptions(options);
    this.table = new Element('table').setProperties(this.options.properties);
    this.tbody = new Element('tbody').injectInside(this.table);
    this.options.rows.each(this.push.bind(this));
  },

  /*
  Property: push
    Add contents to a section of the table.

  Arguments:
    row - (array) content to be added. Each item is a cell.
    section - thead or tfoot, defaults to tbody
    head - (boolean) true to use th instead of td, default is false
  */
  push: function(row, section, head) {
    var tr = new Element('tr').injectInside(section || this.tbody);
    row.each(function (v) {
      var td = new Element(head ? 'th' : 'td').injectInside(tr);
      if (v.properties) td.setProperties(v.properties);
      if (v.content)    td.setContent(v.content);
      else              td.setContent(v);
    });
    return this;
  },

  /*
  Property: pushHead
    Add a row of content to the thead. Uses TRs instead of TDs.

  Arguments:
    row - (array) content to be added. Each item is a cell.
  */
  pushHead: function(row) {
    if (!this.thead) this.thead = new Element('thead').injectTop(this.table);
    this.push(row, this.thead, true);
    return this;
  },

  /*
  Property: pushFoot
    Add a row of content to the tfoot.

  Arguments:
    row - (array) content to be added. Each item is a cell.
  */
  pushFoot: function(row) {
    if (!this.tfoot) this.tfoot = new Element('tfoot').injectInside(this.table);
    this.push(row, this.tfoot);
    return this;
  },

  /*
  Property: pushCaption
  */
  // TODO should be set caption and only allow one
  pushCaption: function(content) {
    new Element('caption').setContent(content).injectTop(this.table);
    return this;
  }
});
Table.implement(new Options);

/*
Class: Autocompleter.Ajax.Json2
  Modified version of Autocompleter.Ajax.Json that uses objects for the response
  instead of a string array.
*/
Autocompleter.Ajax.Json2 = Autocompleter.Ajax.Json.extend({
  updateChoices: function(choices) {
    this.choices.empty();
    this.selected = null;
    if (!choices || !choices.length) return;
    if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
    choices.each(this.options.injectChoice || function(choice, i){
      var el = new Element('li').setHTML(this.markQueryValue(choice.name));
      el.responseObj = choice;
      el.inputValue = choice.name;
      this.addChoiceEvents(el).injectInside(this.choices);
    }, this);
    this.showChoices();
  },

  choiceSelect: function(el) {
    if (el.responseObj.notFound) { this.hideChoices(); return; }
    this.observer.value = this.element.value = el.inputValue;
    this.hideChoices();
    this.fireEvent('onSelect', [this.element, el.responseObj], 20);
  },

  queryResponse: function(resp) {
    //  if the input does not have focus any more then just return
    if (!this.focussed)
    {
        return;
    }
    this.value = this.queryValue = this.element.value;
    this.selected = false;
    this.hideChoices();
    this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
    var choices = Json.evaluate(resp || false);
    if (!choices || !choices.length) {
      choices = [{ name : JS_location_not_found, value: JS_location_not_found, notFound: true}];
    }
    this.updateChoices(choices);
  }
});

// subclass the autocomplete to make it more closely match noreaster behavior
Autocompleter.Ajax.Flights = Autocompleter.Ajax.Json2.extend(
{
  initialize: function(el, url, options) {
    this.parent(el, url, options);
    this.verifyAjax = new Ajax(url, {autoCancel: true, method: 'get'});
    this.verifyAjax.addEvent('onComplete', this.verifyResponse.bind(this));
    this.verifyAjax.addEvent('onFailure', this.verifyResponse.bind(this, [false]));
  },

  // override escape key so that it doesn't hide the list
  onCommand: function(e, mouse) {
    if (e.key && !e.shift && e.key == 'esc') {
      if (mouse && this.focussed) {
        this.prefetch();
      }
      e.stop();
      return;
    } else {
      this.parent(e, mouse);
    }
  },

  // make sure that we capture text in the hidden field
  toggleFocus: function(state) {
    this.parent(state);
    if (!state) {
      if (this.element.value.length) {
        // verify that we have a legal airport
        var data = $extend({}, this.options.postData);
        data[this.options.postVar] = this.element.value;
        this.verifyAjax.request(data);
      } else {
        // make sure to clear all fields
        this.fireEvent('onSelect', [this.element, {name:'',value:''}], false);
      }
    }
  },

  verifyResponse: function(resp) {
    var choices = Json.evaluate(resp || false);
    if (choices && choices.length) {
      this.fireEvent('onSelect', [this.element, choices[0]], false);
      this.element.fireEvent('airportupdate');
    } else {
      this.fireEvent('onSelect', [this.element, {name:'',value:''}], false);
    }
  }
});

/*
Class: TabSet
  The TabSet class creates a group of elements that are toggled when their handles are clicked. When one element toggles in, the others toggle back.

Arguments:
  togglers - required, a collection of elements, the elements handlers that will be clickable.
  elements - required, a collection of elements the transitions will be applied to.
  options - optional, see options below, and <Fx.Base> options and events.

Options:
  show - integer, the Index of the element to show at start.

Events:
  onActive - function to execute when an element starts to show
  onBackground - function to execute when an element starts to hide
*/
var TabSet = new Class({
  options: {
    onActive: Class.empty,
    onBackground: Class.empty,
    show: 0,
    collapsable: false
  },

  initialize: function() {
    var options, togglers, elements;
    $each(arguments, function(argument, i){
      switch($type(argument)){
        case 'object': options = argument; break;
        default:
          var temp = $$(argument);
          if (!togglers) togglers = temp;
          else elements = temp;
      }
    });
    this.togglers = togglers || [];
    this.elements = elements || [];
    this.setOptions(options);
    this.elements.each(function(el, i){
      this.togglers[i].addEvent('click', this.display.bindWithEvent(this, i));
      if (this.options.show === i){
        el.show();
        this.previous = i;
        this.fireEvent('onActive', [null, this.togglers[i], el]);
      } else {
        el.hide();
      }
    }, this);
  },

  /*
  Property: display
    Shows a specific section and hides all others. Useful when triggering a tab from outside.

  Arguments:
    index - integer, the index of the item to show, or the actual element to show.
  */

  display: function(event, index) {
    if (this.options.collapsable && index == this.previous) { // clicked on currently active tab
      if (this.elements[index].hidden()) {
        this.fireEvent('onActive', [event, this.togglers[index], this.elements[index]]);
        this.elements[index].show();
      }
      else {
        this.fireEvent('onBackground', [event, this.togglers[index], this.elements[index]]);
        this.elements[index].hide();
      }
      return this;
    }
    if (this.previous || this.previous === 0) {
      this.elements[this.previous].hide();
      this.fireEvent('onBackground', [event, this.togglers[this.previous], this.elements[this.previous]]);
    }
    this.previous = index;
    this.elements[index].show();
    this.fireEvent('onActive', [event, this.togglers[index], this.elements[index]]);
    return this;
  }
});
TabSet.implement(new Events, new Options);

/*
Class: ToggleSet
  The ToggleSet class creates a group of elements that are toggled when their handles are clicked. When one element toggles in, the others toggle back.
  
Arguments:
  togglers - required, a collection of elements, the elements handlers that will be clickable.
  elements - required, a collection of elements the transistions will be applied to
*/
var ToggleSet = new Class({
  initialize: function(togglers, elements, index) {
    this.index    = index || 0;
    this.togglers = togglers || [];
    this.elements = elements || [];
    this.togglers.each(function(el, i) {
      this.togglers[i].addEvent('click', this.display.bind(this, i));
      this.elements[i].hide();
    }, this);
  },
  
  add: function(toggler, element) {
      var i = this.togglers.length;
      this.togglers.push(toggler);
      this.elements.push(element);
      toggler.addEvent('click', this.display.bind(this, i));
      element.hide();
  },
  
  expand: function(index) {
    this.togglers[index].addClass('open');
    this.elements[index].show();
    return this;
  },
  
  collapse: function(index) {
    this.elements[index].hide();
    this.togglers[index].removeClass('open');
    return this;
  },
  
  display: function(index) {
    if (this.previous == index) {
      if (this.elements[index].hidden()) this.expand(index);
      else this.collapse(index);
    }
    else {
      if (this.previous || this.previous === 0) {
        this.collapse(this.previous);
      }
      this.previous = index;
      this.expand(index);
    }
    return this;
  }
});



/**************************************************************

	Script		: Slider
	Version		: 1.0
	Authors		: Samuel Birch
	Desc		: Slider bar.
	Licence		: Open Source MIT Licence
	Website		: http://www.phatfusion.net/index.htm
	
**************************************************************/

var Slider = new Class({

	options: {
		onChange: Class.empty,
		onComplete: Class.empty,
		onTick: function(pos){
			this.knob.setStyle(this.p, pos);
		},
		mode: 'horizontal',
		steps: 100,
		offset: 0
	},

	initialize: function(el, knob, options){
		this.element = $(el);
		this.knob = $(knob);
		this.setOptions(options);
		this.previousChange = -1;
		this.previousEnd = -1;
		this.step = -1;
		this.element.addEvent('mousedown', this.clickedElement.bindWithEvent(this));
		var mod, offset;
		switch(this.options.mode){
			case 'horizontal':
				this.z = 'x';
				this.p = 'left';
				mod = {'x': 'left', 'y': false};
				offset = 'offsetWidth';
				break;
			case 'vertical':
				this.z = 'y';
				this.p = 'top';
				mod = {'x': false, 'y': 'top'};
				offset = 'offsetHeight';
		}
		this.max = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.half = this.knob[offset]/2;
		this.getPos = this.element['get' + this.p.capitalize()].bind(this.element);
		this.knob.setStyle('position', 'relative').setStyle(this.p, - this.options.offset);
		var lim = {};
		lim[this.z] = [- this.options.offset, this.max - this.options.offset];
		this.drag = new Drag.Base(this.knob, {
			limit: lim,
			modifiers: mod,
			snap: 0,
			onStart: function(){
				this.draggedKnob();
			}.bind(this),
			onDrag: function(){
				this.draggedKnob();
			}.bind(this),
			onComplete: function(){
				this.draggedKnob();
				this.end();
			}.bind(this)
		});
		if (this.options.initialize) this.options.initialize.call(this);
	},

	/*
	Property: set
		The slider will get the step you pass.

	Arguments:
		step - one integer
	*/

	set: function(step){
		this.step = step.limit(0, this.options.steps);
		this.checkStep();
		this.end();
		this.fireEvent('onTick', this.toPosition(this.step));
		return this;
	},
	
	setDefault: function(step){
		if (step > this.options.steps) step = this.options.steps;
		else if (step < 0) step = 0;
		this.step = step;
		//this.checkStep();
		this.end();
		this.knob.setStyle(this.p, this.toPosition(this.step)+'px');
		return this;
	},

	scrolledElement: function(event){
		if (event.wheel < 0) this.set(this.step + 1);
		else if (event.wheel > 0) this.set(this.step - 1);
		event.stop();
	},

	clickedElement: function(event){
		var position = event.page[this.z] - this.getPos() - this.half;
		position = position.limit(-this.options.offset, this.max -this.options.offset);
		this.step = this.toStep(position);
		this.checkStep();
		this.end();
		//this.fireEvent('onTick', position);
		this.set(this.step);
	},

	draggedKnob: function(){
		this.step = this.toStep(this.drag.value.now[this.z]);
		this.checkStep();
		this.setDefault(this.step);
	},

	checkStep: function(){
		if (this.previousChange != this.step){
			this.previousChange = this.step;
			this.fireEvent('onChange', this.step);
		}
	},

	end: function(){
		if (this.previousEnd !== this.step){
			this.previousEnd = this.step;
			this.fireEvent('onComplete', this.step + '');
		}
	},

	toStep: function(position){
		return Math.round((position + this.options.offset) / this.max * this.options.steps);
	},

	toPosition: function(step){
		return this.max * step / this.options.steps;
	}

});

Slider.implement(new Events);
Slider.implement(new Options);
/*
Script: Behavior.js
  Behavior rules and related functions.
  Contains: <Behavior>
*/

var rules = {};
var rulesN = {};
var ajaxRules = {};

/*
Class: Behavior
  Custom class to help manage behavior stylish rules. Use register to register
  rules at page loading. Use reload to re-apply rules on element updated after
  an AJAX update.
 */
var Behavior = new Class({
  /*
  Property: register
    Applies the rules to the current DOM.

  Arguments:
    rules - (array) map of selectors to the functions that operate on them
  */
  register: function(rules){
    this.rules = rules;
    for (var i in this.rules) {
      this.reload(i);
    }

    for (var id in rulesN) {
      this.reloadN(id);
    }

    return this;
  },

  /*
  Property: reload
    Applies a single rule to the current DOM

  Arguments:
    rule - (string) selector that should be applied
  */
  reload: function(rule){
    elements = $$(rule);
    for(y=0;y<elements.length;y++){
      this.rules[rule](elements[y]);
    }
    return elements.length;
  },
  
  reloadN: function(id){
    var i = 0;
    while (n=$(id+(i++))) {
      rulesN[id](n);
    }
    return i - 1;
  },

  /*
  Property: apply
    Applies all registered rules to a subset of the DOM
  
  Arguments:
    elmt - (Element) DOM node to apply rules to
   */
  apply: function(elmt){
    // Process links - don't use rules[] because it is too slow for links.
    processLinks(elmt);
    
    for (var rule in this.rules) {
      window.applyLastRule = rule;
      elmt.getElements(rule).each(function(e){
        this.rules[rule](e);
      }, this);
    }

    for (var rule in ajaxRules) {
      window.applyLastRule = rule;
      elmt.getElements(rule).each(function(e){
        ajaxRules[rule](e);
      }, this);
    }
  }
});
window.behavior = new Behavior();


// func: doCookieCheck
//   Checks whether session cookies are accepted
//
// Returns:
//   true if session cookies are allowed
//
function doCookieCheck()
{
	// Session cookies not accepted
  Cookie.set('SessionTest', 'true', {duration: 0});
  if (Cookie.get('SessionTest')) {
    Cookie.remove('SessionTest');
    return true;
  }
  return false;
}
if (window.showPopup) showPopup();

window.addEvent('load', function() {
  Cookie.remove('NPID');

  //internet explorer fires onload event for every iframe, we only want top window
  if(window == window.top) {  
    var c = Cookie.get('ajaxAction');
    if (c) {
      Cookie.remove('ajaxAction', {domain:cookieDomain, path:"/"});
      c = c.split('|');
      if (window[c[0]]) window[c[0]].attempt(c.slice(1));// 0 - function to call, 1..N - parameters as a strings
    }
  }

  // Firefox and Safari calculate the scroll offset before content is collapsed
  // so make sure to scroll to the proper place once everything is loaded
  if (window.gecko || window.webkit) { 
    var id = window.location.hash;
    if (id.length > 1) id = id.substring(1);
    var t = $(id);
    if (t && t.getTop() != window.getScrollTop()) window.scrollTo(0, t.getTop());
  }
  
  // show any DHTML popup from the PopupManager.java
  if (typeof showDHTMLOverlayOnLoad != "undefined") {
      showDHTMLOverlayOnLoad();
  }
  
  if (typeof cpu_run != "undefined") cpu_run();
  
  if (/^#save/.test(location.hash)) { // saves post login popup
    doSavesPostLoginPopup();
  }
});

// Define the ready event - the point at which to load the behavior.
// This may need to be load instead of domready with cerain badly-behaving ads.
TAReadyEvent = window.TAReadyEvent || "domready";

// apply the rules when the DOM is ready.
window.addEvent(TAReadyEvent, behaviorFunction);

var bfCount = 0;
function behaviorFunction()
{
  if( typeof performancePingbackInit != 'undefined' )
  {
    performancePingbackInit.behaviorStart = new Number( new Date() );
  }

  if (window.ie &&  (!$('FOOT') || !$('FOOT').readyState || $('FOOT').readyState != 'complete') && bfCount < 10) {
    bfCount++;
    behaviorFunction.delay(30);
    return;
  }
  
  // hide some elements
  if (window.hideOnLoad) $A(hideOnLoad).each(function(v){ var x = $(v); if (x) x.hide(); });

  // grab focus of the search bar
  searchFocus();
  
  // Process links - don't use rules[] because it is too slow for links.
  processLinks(document);

  // register the rules
  behavior.register(rules);
  
  //  If the location hash is set to '#Calculator' and we are on a valid
  //  servlet, open the calculator
  var main = $('MAIN');
  if ((window.location.hash == '#Calculator') &&
      ($('HOMEPAGE') ||                               //  Home Page
       (main && main.hasClass('VacationRentals')) ||  //  /VacationRentals
       $('HOTEL_LANDER')))                            //  /Hotels
  {
      //  this is a bit hacky, but don't allow other popups to open on page load.
      //  it has already been added to an onload queue so we will replace it
      //  with a no-op function.  The downside here is that we still have a cookie
      //  set as if they saw the popup but they never do.  Based on the usage of 
      //  this direct link PM (Theresa) said it is ok.
      showDHTMLOverlayOnLoad = function(){};

      //  open the calculator
      vrCalculator();
  }
  
  // setup the accordion if need be
  var tabs = $$('#DEST_HOME #DEST_ACCORDION .window');
  if (tabs.length > 0) {
    var elmts = $$('#DEST_HOME #DEST_ACCORDION .pane');
    new TabSet(tabs, elmts, {
      show: 0,
      collapsable: true,
      onActive: function(event, toggler, element) {
        //if (!toggler.hasClass('first')) new Ajax('/ActionRecord?action='+toggler.id).request();
        toggler.addClass('active');
      },
      onBackground: function(event, toggler, element) {
        toggler.removeClass('active');
      }
    });
  }
  
	var sisTabs = $$('#SIS_ACCORDION .window');
	if (sisTabs.length > 0){
	  var sisElmts = $$('#SIS_ACCORDION .pane');
    // show the first pane with content
    var showIndex = 0;
    for (var i=0; i<sisElmts.length; i++)
    {
      if (sisElmts[i].getElement('.sisContribution'))
      {
        showIndex = i;
        break;			
      }
    }
	  new TabSet(sisTabs, sisElmts, {
	    show: showIndex,
	    onActive: function(event, toggler, element){
	      toggler.addClass('active');
	      element.addClass('active');
        if (element.getSize().size.y > 160)
        {
          element.setStyles({height:160, overflow:'auto'});
        }
	    },
	    onBackground: function(event, toggler, element){
	      toggler.removeClass('active');
	      element.removeClass('active');
	    }
	  });
	}

  if( typeof performancePingbackInit != 'undefined' && performancePingbackInit.enabled )
  {
    performancePingbackInit.behaviorEnd = new Number( new Date() );
    var loadTime = performancePingbackInit.behaviorStart - performancePingbackInit.start;
    var behaviorTime = performancePingbackInit.behaviorEnd - performancePingbackInit.behaviorStart;

    var uri = '/PerformancePingback'+
        '?ppuid='+ performancePingbackInit.uid +
        '&load='+ loadTime +
        '&behavior='+ behaviorTime +
        '&servlet='+ performancePingbackInit.servlet;

    new Ajax(uri).request();
  }

  // Check for a fresh VS cookie
  // bug: 29564  gbelote
  if( typeof freshVSTrackingCookie != 'undefined' && freshVSTrackingCookie )
  {
    // Check to see if VS tracking cookie exists
    var hasVSCookie = Cookie.getRaw ('v1st');
    if (hasVSCookie)
    {
        // Fire an AJAX request
        new Ajax ('/VSCookieRequest').request ();
    }
  }
}

// Setup email replacement. e.g. #eobf( 'dmosales'  '.com')  -> mailto: 
rulesN['EOBF'] = function(elmt) {
  var e = elmt.innerHTML+'@tripadvisor'+elmt.title;
  elmt.replaceWith(new Element('a', {href: 'mailto:'+e}).setContent(e));
}

/*
Function: popup
  Show a poup window. The event target (or one of its ancestors) should be a
  link. The href of the link will be used as the URL of the popup

Arguments:
  e       - the event
  width   - width of the popup
  height  - height of the popup
  x       - horizontal screen offset
  y       - vertical screen offset
 */
var popupIndex = 0;
function popup(e, name, width, height, x, y, noScroll, buildUrl, allOptions)
{
  new Event(e).preventDefault();
  var ops = "";
  if (sz = this.className.match(/sz(\d+)x(\d+)/)) {
    width = sz[1];
    height = sz[2];
  }
  if (this.href.indexOf("p=HotelsCom") > -1) {
    width = Math.max(width, 960);
    height = Math.max(height, window.getHeight(), 600);
  }
  if (width) ops += ",width="+width;
  if (height) ops += ",height="+height;
  if (x) ops += ",screenX="+x+",left="+x;
  if (y) ops += ",screenY="+y+",top="+y;
  if (ops != "") {
    if (allOptions) {
      ops = "toolbar=1,resizable=1,menubar=1,location=1,status=1,scrollbars=1" + ops;
    }
    else {
      ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=" + (noScroll ? 0 : 1) + ops;
    }
  }
  var w = window.open(buildUrl ? buildUrl : this.href, name || "p"+window.name+(popupIndex++), ops);
  if (w != null)
  {
    try {w.opener = self;} catch (exc) {}
    w.focus();
  }
}

// 'this' is the element
function formSubmit(e)
{
  new Event(e).preventDefault();
  this.click();
}

function unobf(uri)
{
  return (uri.substring(0,8) == "NOFOLLOW" ? "http://" + uri.substring(8) : "/" + uri) + ".html";
}
/*
Function: toggle
  Toggle the class (default: 'off') of the element this function is bound to.
*/
function toggle(e) {new Event(e).preventDefault(); this.toggleClass('off');}

/* Some generic rules
   ---------------------------------------------------------------------------------------------- */

// called when a link with a js_ class is clicked.
var doBehavior = function(e) {
  var e = new Event(e || window.event);
  var elmt = $(e.target);
  if (elmt.getTag() != "a") elmt = elmt.getParent("a");
  if (/pid(\d+)/.test(elmt.className)) Cookie.set('NPID', RegExp.$1, {domain: cookieDomain, time:5});
  $pick(elmt.className.match(/(js_\w+)/g), []).each(function(c) {
    linkMap[c](elmt, e);
  });
}

function _processLink(elmt) {
  if (elmt.className.indexOf('js_') >= 0 || elmt.className.indexOf('pid') >= 0) {
    if (elmt.addEventListener) elmt.addEventListener('click', doBehavior, false);
    else elmt.attachEvent('onclick', doBehavior);
  }
}

var setPID = function(pid) {
   Cookie.set('NPID', pid, {domain: cookieDomain, time:5})
}

/* All Link rules in one function for performance */
function processLinks(root) 
{
  var aTags = $A(root.getElementsByTagName('a'));
  aTags.each(_processLink);
  return aTags.length;
}

function searchFocus()
{
  if($('mainSearch'))
  {
    try {
      $('mainSearch').focus();
    } catch(e) { }
  }
}

var linkMap = {};

// submit forms via AJAX
linkMap['js_ajaxSubmit'] = function(elmt, e) {
  var uri = elmt.href;
    if (!elmt.className.match(/\bform_(\w+)\b/)) return;
    var form = $(RegExp.$1);
    var ops = {
        data: form,
        onFailure: function(e)
        {
            if (form) form.removeClass('ajaxInFlight');
            alert(JS_Ajax_failed);
        }
    }
    var cbfn = window['callback'+form.id]; 
    if (elmt.className.match(/\method_(\w+)\b/)) ops.method = RegExp["$1"];
    if (elmt.className.match(/\btgt_(\w+)\b/)) ops.update = RegExp["$1"];
    else ops.onComplete = function(text, xml)
    {
        if (form) form.removeClass('ajaxInFlight');
        ta.overlays.showInLightbox(text);
        if (
          $defined(cbfn) &&
          $type(cbfn) == "function" )
        {
          cbfn();
        }
    }
  new Event(e).preventDefault();
  var fn = window['validate'+form.id]; 
  if (
    $defined(fn) &&
    $type(fn) == "function" &&
    fn.apply(form) == false )
        return;
  form.addClass('ajaxInFlight');
  if (elmt.className.match(/\bevalScripts\b/)) ops.evalScripts = true;  
  new Ajax(uri, ops).request();
};

// used in translations: newsletter_9b5, newsletter_9b6
linkMap['js_ajax'] = function(elmt, e) {
  new Event(e).preventDefault();
  if (!(tgt = elmt.className.match(/\btgt_(\w+)\b/))) return;// target is required
  tgt = $(tgt[1]);
  new Ajax(elmt.href, {
    update: tgt,
    onFailure: function(e) { alert(JS_Ajax_failed); }
  }).request();
};

// like 'ajax', but expected to log-in first
linkMap['js_ajaxlogin'] = function(elmt, e) {
  new Event(e).preventDefault();
  if (!(tgt = elmt.className.match(/\btgt_(\w+)\b/))) return;// target is required
  var alHref = elmt.href;
  tgt = $(tgt[1]);
  new Ajax(elmt.href, {
    onComplete: function(txt, xml) {
      var bWasLoggedIn = (txt.indexOf('<!--nologin-->')<0);
      if(!bWasLoggedIn)
      {
          login(['tt','ajax','returnTo', alHref + "&rd=1", 'greeting', 'showuserreviews_vote_25ee' ]);
      }
      else
      {
          tgt.innerHTML = txt;
      }
    },
    onFailure: function(e) { alert(JS_Ajax_failed); }
  }).request();
};


linkMap['js_ajaxMsg'] = function(elmt, e) {
  if (elmt.className.match(/\bcallback_ShowRecaptcha\b/))
  {
    new Asset.javascript('http://api.recaptcha.net/js/recaptcha_ajax.js');
  }
  ajaxMsg.attempt(e, elmt);
};

linkMap['js_ajaxReport'] = function(elmt, e) {
  ajaxReport.attempt(e, elmt);
};

// open links in popups of various sizes
linkMap['js_popup'] = function(elmt, e) { popup.attempt(e, elmt); };
linkMap['js_popComm'] = function(elmt, e) { if (typeof pageTracker != "undefined") { var utmp = "/"+elmt.href.replace(/^http:\/\/.+\//i, ""); pageTracker._trackPageview(utmp); } popup.attempt([e, null, 950, 610, null, null, null, null, true], elmt); };
// FIXME: tourism-narrow only
linkMap['js_popNoScroll'] = function(elmt, e) { popup.attempt([e, null, null, null, null, null, true], elmt); };
linkMap['js_popCR'] = function(elmt, e) { popup.attempt([e, 'cr', 245, 610, 5, 5], elmt); };
linkMap['js_email'] = function(elmt, e) {  popup.attempt([e, 'email', 580, 460, 30, 25], elmt); };
// FIXME: SUR only
linkMap['js_popReview'] = function(elmt, e) { popup.attempt([e, 'review', 550, 395, 30, 25], elmt); };
linkMap['js_popPhoto'] = function(elmt, e) { popup.attempt([e, 'photo', 650, 350], elmt); };
linkMap['js_popGallery'] =  function(elmt, e) { Cookie.set('PhotoPop', '1', {domain: cookieDomain}); popup.attempt([e, 'media', 782, 820], elmt); };
linkMap['js_popGalleryComm'] = function(elmt, e) { Cookie.set('PhotoPop', '1', {domain: cookieDomain}); popup.attempt([e, 'media', 780, 950], elmt); };
linkMap['js_popDMO'] = function(elmt, e) { popup.attempt([e, 'dmo', 400, 400], elmt); };
// FIXME: nexus only
linkMap['js_popNxTall'] = function(elmt, e) { popup.attempt([e, 330, 680], elmt); };
linkMap['js_popNxWide'] = function(elmt, e) { popup.attempt([e, 730, 380], elmt); };
linkMap['js_popNxLogin'] = function(elmt, e) { popup.attempt([e, 'login', 795, 645], elmt); };
linkMap['js_popFraud'] = function(elmt, e) { popup.attempt([e, 'fraud', 640, 460], elmt); };
linkMap['js_popDestGd'] = function(elmt, e) { popup.attempt([e, 'dest_guide', 565, 700, (screen.width-700)/2, (screen.height-600)/2], elmt); };
linkMap['js_tamgSubsDestGd'] = function(elmt, e) {
  window.showTamgSubsLB = function() { 
    new Ajax('/TAMGSubOffers?t=DG_LB', {
      onComplete: function(txt, xml) { ta.overlays.showInLightbox(txt); },
      method: 'get',
      evalScripts: true
    }).request(); };
};

linkMap['js_popPromo'] = function(elmt, e) { popup.attempt([e, 'promo', 600, 700, 30, 25], elmt) };

linkMap['js_popSmall'] = function(elmt, e) {
  if (screen.width > 1024)     { popup.attempt([e, null, 800, 600, 240, 5, false, false, true], elmt); }
  else if (screen.width > 800) { popup.attempt([e, null, 620, 500, 240, 5, false, false, true], elmt); }
  else                         { popup.attempt([e, null, 475, 390, 210, 5, false, false, true], elmt); }
  return elmt;
};

linkMap['js_popProfPhoto'] = function(elmt, e) {
  if (screen.width > 1024)     { popup.attempt([e, null, 800, 500, 240, 5, false, false, true], elmt); }
  else if (screen.width > 800) { popup.attempt([e, null, 620, 450, 240, 5, false, false, true], elmt); }
  else                         { popup.attempt([e, null, 475, 390, 210, 5, false, false, true], elmt); }
  return elmt;
};

linkMap['js_popPartner'] = function(elmt, e) {
  if (screen.width > 1024)     { popup.attempt([e, null, 980, 500, 240, 5, false, false, true], elmt); }
  else if (screen.width > 800) { popup.attempt([e, null, 800, 500, 240, 5, false, false, true], elmt); }
  else                         { popup.attempt([e, null, 620, 450, 210, 5, false, false, true], elmt); }
  return elmt;
};

linkMap['js_noTAPD'] = function(elmt, e) { Cookie.remove('TAPD', {domain: cookieDomain}); };

// FIXME: only used on nexus
linkMap['js_modifySub'] = function(elmt, e) { modifySub.apply(elmt, [e]); };

// FIXME: only used in compose/send message lightbox layer - THIS DOES NOT WORK W/ LINK ASSUMPTION
ajaxRules['a.js_delay'] = function(elmt) {
  if (!(ms = elmt.className.match(/\b(\d+)ms\b/))) return;
  ms = parseInt(ms[1]);
  elmt.fireEvent('click', null, ms);
};

// TODO: remove this once the new dhtml popup framework gets out of pool testing
ajaxRules['a.figsSurveyLink']=function(elmt, e) {
  Cookie.set('TAPanelSurveyPopup', '-1', {domain:cookieDomain, path:"/", duration:365});
}


rules['a#TERMS'] = function(elmt) {  elmt.addEvent('click', popup.bindAsEventListener(elmt, ['terms',       300, 300, 30, 25]));};

// IE6 does not support :hover on elements other than <A>
// use .hvrIE6 and style with .hvrIE6.mseOvr
if (window.ie6 && window.pageServlet != 'Hotel_Review')
{
  var hvrIE6Fn = function(elmt)
  {
    elmt.addEvent('mouseenter', function() { elmt.addClass('mseOvr'); });
    elmt.addEvent('mouseleave', function() { elmt.removeClass('mseOvr'); });
  }
  rules['span.hvrIE6'] = hvrIE6Fn;
  rules['label.hvrIE6'] = hvrIE6Fn;
  rules['h2.hvrIE6'] = hvrIE6Fn;
}


// Disabled - For next sponsorship, target using an ID and move to page-specific JS file(s)
/*rules['area.js_popSmall'] = function(elmt) {
  elmt.addEvent('click', function() {
    if (screen.width > 1024)     { popup.attempt([null, 800, 600, 240, 5, false, false, true], elmt); }
    else if (screen.width > 800) { popup.attempt([null, 620, 500, 240, 5, false, false, true], elmt); }
    else                         { popup.attempt([null, 475, 390, 210, 5, false, false, true], elmt); }
  });
}*/

// used by TW and MU callouts on LHS, TCH2009
// text elements with class 'focusClear' have their value cleared on focus if it is the default value
// Sep 09 Added onBlur - restores default if input is empty
//rules['#NEWSLETTER_CALLOUT input.focusClear[type=text]'] = 
rules['#USER_REVIEW_FORM input.focusClear[type=text]'] = rules['#TCH input.focusClear[type=text]'] = rules['#TCH_POP_FRM input.focusClear[type=text]'] = rules['#TCH2009_WIDGET input.focusClear[type=text]'] = rules['#HOMEPAGE input.focusClear[type=text]'] = rules['#homePageVRCalcPromo input.focusClear[type=text]'] = rules['#WHS_MAIN input.focusClear'] =function(elmt) {

  elmt.addEvent('focus', function()
  {
    if (elmt.value == elmt.defaultValue)
    {
      elmt.removeClass('focusClear');
      elmt.value = '';
    }
  });

  elmt.addEvent('blur', function()
  {
    if (elmt.value == "")
    {
      elmt.addClass('focusClear');
      elmt.value =  elmt.defaultValue;
    }
  });
}

rules['#NEWSLETTER_CALLOUT input.memberUpdateEmail[type=text]'] = function(elmt) {
  $('memberUpdatesEmailDefault').hide();
  $(elmt.form).addEvent('submit', function(e) {
  	if(elmt.value == $('memberUpdatesEmailDefault').innerHTML)
  	{
  		elmt.value = ""; 		
  	}
  });
}

// don't try to gain focus of search box on any forum servlets
/* bug 28219  gbelote
rules['#SEARCH_LHN'] = function(elmt) {
  if(pageServlet != 'ShowForum' 
    && pageServlet != 'ShowTopic'
    && pageServlet != 'ForumHome'
    && pageServlet != 'ListForums' 
    && pageServlet != 'NewTopic' 
    && pageServlet != 'PostReply')
  {
	  window.addEvent('load', function() {
		  try { elmt.getElement('input[type=text]').focus(); } catch(e) { }
	  });
  }
  elmt.getElement('form').addEvent('submit', function(e) {if (elmt.value == '') (new Event(e)).stop();});
}
*/

// text elements with class 'alertIfEmpty' cause form submission to fail and an alert to be shown if the value is empty
// not targetted better because form can occur twice on BYG
var alertIfEmpty = function(elmt) {
  var form = $(elmt.form);
  if (form) form.addEvent('submit', function(e) { if (elmt.value == '') {
     if(msg = elmt.className.match(/\bmsg_([\w\d]+)\b/)) {
        alert(window[msg[1]]);
     } else {
        alert('The field can not be empty'); // input fields should instead use a localization key
     }
     (new Event(e)).stop();
   }});
}
rules['#NX_NEW_TITLE input, #NX_NEW_TITLE2 input'] = alertIfEmpty;

rules['select.submitOnChange'] = function(elmt) {
  elmt.addEvent('change', function() {elmt.form.submit();});
}

// MOVE - only used on Fun Stuff landing page
// rules['select.redirOnChange'] = function(elmt) {
//   elmt.addEvent('change', openValueInNew.bindAsEventListener(elmt));
// }

// clicking the 'search within' checkbox should set the 'exc' form field to 'n' when selected, 'y' otherwise
//rules['.navSrch input#geo'] = function(elmt) {
//  elmt.addEvent('change', function() {$('exc').value = elmt.checked ? 'n' : 'y';});
//}

// collapsible content - hide initially
var toggleMeRule = function(elmt) {
  if( false == elmt.hasClass('defaultOpen') ) {
    elmt.addClass('off');
  }

  elmt.getElement('.show').addEvent('click', toggle.bindAsEventListener(elmt));
  elmt.getElements('.hide').addEvent('click', toggle.bindAsEventListener(elmt));
}
rules['#TOGGLEME'] = toggleMeRule; // to appease FF's XPath
rules['#TOGGLEME2'] = toggleMeRule;

// used by DHTML pop-ups
// .js_swapBlocks is an open-only toggle
ajaxRules['div.js_swapBlocks'] = function(elmt) {
  var swapOut = elmt.getElement('.js_swapOut');
  var swapIn = elmt.getElement('.js_swapIn');
  var swapTrigger = elmt.getElement('a.js_swapTrigger');
  var swapBackTrigger = elmt.getElement('a.js_swapBackTrigger');
  if(swapOut && swapIn && swapTrigger) 
  {
     swapTrigger.addEvent('click',  function(e) { new Event(e).preventDefault(); window.removedBit = swapOut.remove(); swapIn.show(); });
     swapTrigger.removeClass('js_swapTrigger');
  }
  if(swapOut && swapIn && swapBackTrigger) 
  {
     swapBackTrigger.addEvent('click',  function(e) { new Event(e).preventDefault(); swapIn.hide(); $('parentOfSwapOut').appendChild(window.removedBit); });
     swapBackTrigger.removeClass('js_swapBackTrigger');
  }
}

// MOVE - only used by nexus
// Nexus - subscribe/unsubscribe
var modifySub = function(e) {
  (new Event(e)).preventDefault();
  var elmt = this;
  new Json.Remote(this.href, {
    onFailure: function(e) { alert(JS_Ajax_failed); },
    onComplete: function(rslt) {
      if (rslt && rslt.debug && rslt.editErrorTag)
        alert("Error during ajax call, \nTag: " + rslt.editErrorTag + "\nMsg: " + rslt.editErrorMessage + "\nContent: " + data.editErrorContent);
      else if (rslt.readonly) document.location = rslt.maintenanceUrl;
      else if (rslt.loginUrl) document.location = rslt.loginUrl;
      else {
        var i = elmt.getParent().id;
        if (/^un/.test(i)) i = i.substring(2);
        else i = "un" + i;
        elmt.getParent().hide();
        $(i).show();
      }
    }
  }).send();
}

// MOVE - only used by a couple servlets
// type ahead
rules['input.typeAhead[type=text]'] = function(elmt) {
 if (action = elmt.className.match(/\bact(\w+)\b/)) {
   var onSelectFunc = elmt.className.match(/\bonSel-([\w\.]+)\b/);
   if ((onSelectFunc != null) && (onSelectFunc.length > 0))
   {
       onSelectFunc = eval(onSelectFunc[1]);
   }
   
   //  check if we have a custom zIndex
   var zIndex = 42; //  42 is the default in Autocompleter.js
   var zIndexClass = elmt.className.match(/zIndex-(\d+)/);
   if (zIndexClass != null)
   {
       zIndex = zIndexClass[1];
   }
   
   new Autocompleter.Ajax.Json2(elmt, "/TypeAheadJson?action="+action[1], {
     ajaxOptions: {method:'get'},
     postVar: 'query',
     inheritWidth: false,
     'zIndex':zIndex,
     onSelect: function(elemt, resObj) {
       if (onSelectFunc) {
         onSelectFunc(elemt, resObj, elmt);
       }
     }
   });
 }
}

// used in DHTML pop-up
ajaxRules['#PM_UNBLOCK'] = function(elmt) {
  elmt.addEvent('submit', function(e) {
    new Event(e).stop();
    var form = $('PM_UNBLOCK');
    if (form.unblock.value == '1') {
      new Ajax('/SendMessageRD', {
        data: form,
        onComplete: ta.overlays.showInLightbox,
        onFailure: function(e) { alert(JS_Ajax_failed); }
      }).request();
    }
    else {
      $('unblock-intercept').hide();
      $('blockedreply').show();
      form.unblock.value = '1';
    }
  });
}

// Tank-Of-Gas city type ahead form
//rules['#togGeoSearch'] = function(elmt) {
//  elmt.addEvent('click', function(e) {
//    var form = $("TOG_FORM");
//    if (form.elements.q.value == "Enter City")
//    {
//        form.elements.q.value = "";
//    }
//  });
//}

// members only (member benefits) tank-of-gas form
//rules['#TRAVEL_GUIDES_LANDER #TOG_FORM'] = function(elmt) {
//  elmt.addEvent('submit', function(e) {
//    processTOGForm(e);
//  });
//}

// members only (member benefits) guide request form
//rules['#MOG_FORM'] = function(elmt) {
//  elmt.addEvent('submit', function(e) {
//      processGuideRequest(e);
//  });
//}

// members only (member benefits) subscription form
// rules['#MEMONLY_SUBS_FORM'] = function(elmt) {
//   elmt.addEvent('submit', function(e) {
//       processMOSubscribeRequest(e);
//   });
// }

function enableCommunity(callback, noHandle)
{
  var res = callback;
  if (!noHandle) res = function(r) {enableCommunityResponse(r, callback);}
  new Ajax("/MemberSubscriptionsController?Action=ChangeM2M&set=on", {
    onComplete: res,
    onFailure: function(e) { alert(JS_Ajax_failed); }
  }).request();
}

function enableCommunityResponse(res, callback) {
  if(txt.match(/^{/)) {
    var data = eval( '(' + txt + ')' );
    if(data.ERROR) {
      document.getElementById('inviteStatusField').innerHTML = data.ERROR;
      document.getElementById('inviteStatusField').style.display='block';
    }
  } else {
    document.getElementById('inviteStatusField').innerHTML = JS_community_on;
    document.getElementById('inviteStatusField').style.display='block';
    //setTimeout(function() {hide('confirmBubble'); callback();}, 1000);
  }
}

var communityLightbox = function(uri, sCBFuncName) {
  if (!userLoggedIn || migrationMember) return;
  if (!communityEnabled) {
    window['callback_communityLightboxEnable'] = function() {
      communityEnabled = true;
      var lb = ta.retrieve('overlays.current');
      if (lb) lb.hide();
      communityLightbox(uri, sCBFuncName);
    };
    var aSMC = new StringBuffer();
    aSMC.append('<p id="inviteStatusField" style="display: none; color: red;"></p>');
    aSMC.append('<p>'+JS_mem_travelnet_friends_disabledM2M+'.</p>'); // You have disabled member-to-member communications
    aSMC.append('<p><a href="javascript:void(0)" onclick="enableCommunity(window[\'callback_communityLightboxEnable\'], true); return false;">'+JS_common_Clickhere+'</a> '+JS_mem_travelnet_friends_turnOn+'.</p>'); // Click here to turn the feature back on and send your message
    ta.overlays.showInLightbox(aSMC.toString());
    return;
  }
  if (sCBFuncName)
  {
    var callback_communityLightbox = function(r) {
      ta.overlays.showInLightbox(r);
      var cbfn = window[sCBFuncName];
      if (
          $defined(cbfn) &&
          $type(cbfn) == "function" )
      {
        cbfn();
      }
    };
  }
  else
  {
    var callback_communityLightbox = function(r) {
      ta.overlays.showInLightbox(r);
    };
  };
  var ops = {
      onComplete: callback_communityLightbox,
      evalScripts: true,
      onFailure: function(e)
      {
        alert(JS_Ajax_failed);
      }
  };
  new Ajax(uri, ops).request();
}

function getRelativeURL() {
  return window.location.pathname + window.location.search + window.location.hash;
}

var ajaxMsg = function(e) {
  new Event(e).preventDefault();
  var sCBFuncName;
  if (this.className.match(/\b(callback_ShowRecaptcha)\b/))
  {
    sCBFuncName = RegExp["$1"];
  }
  if (!userLoggedIn || migrationMember) {
    if (!(ops = this.className.match(/\bpm(\d)([A-F0-9]+)\b/))) return;
    var msgType = parseInt(ops[1]) == 1 ? "cc" : "cm";
    var greeting = msgType == "cm" ? "m2m_singin_greeting_d99" : "";
    var uid = ops[2];
    var cookieVal = "communityLightbox|" + this.href;
    if (sCBFuncName)
    {
      cookieVal += "|" + sCBFuncName;
    }
    Cookie.set('ajaxAction', cookieVal, {domain:cookieDomain, path:"/"});
    var func = !userLoggedIn ? login : migrate;
    func(['tt',msgType,'greeting',greeting,'returnTo', getRelativeURL()]);
    return;
  }
  communityLightbox(this.href, sCBFuncName);
}

var ajaxReport = function(e) {
  new Event(e).preventDefault();
  ta.overlays.loadInLightbox(this.href);
}

var follow = function(id) {return function(){window.location = $(id).href;}}
//rules['#REVIEWS span.fkLnk,#TOURISMREVIEWS span.fkLnk'] = function(elmt) {
//  if (/\bt([\w\d]+)\b/.test(elmt.className)) elmt.addEvent('click', follow(RegExp.$1));
//}

rules['#dhtmlPopupClose']=function(elmt) {
  elmt.addEvent('click', function() {
  	 $('DHTMLPOPUP').remove();
     $('dhtmlPopupIframe').remove();
   });
}

var reviewRating = new Class(
{
	initialize: function(userId, rating) 
	{
		this.userId = userId;
		this.rating = rating;
	}
});

var dualSliderRule = function(elmt) 
{
  var sDivOne = elmt.getElement('.sOne');
  var sDivTwo = elmt.getElement('.sTwo');
  var minMax = elmt.getElement('.minMax');
  var sMin = elmt.getElement('.rangeMin');
  var sMax = elmt.getElement('.rangeMax');
  var sLeft = elmt.getElement('.sLeft');
  var sRight = elmt.getElement('.sRight');

  var sldrSteps = parseInt(elmt.className.match(/s(\d+)/)[1]);
  var sldrMin = parseFloat(elmt.className.match(/mn([\d.]+)/)[1]);
  var sldrMax = parseFloat(elmt.className.match(/mx([\d.]+)/)[1]);
  var selMin = parseFloat(elmt.className.match(/smin([\d.]+)/)[1]);
  var selMax = parseFloat(elmt.className.match(/smax([\d.]+)/)[1]);
  var sName = elmt.className.match(/name(\w+)/)[1];
  var maxAllInclusive = elmt.hasClass('maxAllInclusive');
  var sOffset = /off(\d+)/.test(elmt.className) ? parseInt(RegExp.$1) : 1;

  if(elmt.className.match(/cur(\d+)/))
  {
    crncy = currencyCodes[ elmt.className.match(/cur(\d+)/)[1] ];
  }

  var doRounding = elmt.className.match(/(round)/);
  if(doRounding) doRounding = true;

  if (sDivOne && sDivTwo)
  {
    var slider = new DualSlider(elmt, sDivOne, sDivTwo, {
      "maxAllInclusive":maxAllInclusive,
      
      onChange: function() {
        var valOne = sliderStepToValue(sldrMin, sldrMax, sldrSteps, slider.knobOne.step, slider.options.round);
        var valTwo = sliderStepToValue(sldrMin, sldrMax, sldrSteps, slider.knobTwo.step, slider.options.round);
        
        //  if the values have not changed then mark the hidden field value accordingly (since we use 0 & 999999 to 
        //  indicate no selection as opposed to the boundary values which imply an explicit selection).
        var minMaxLow = valOne < valTwo ? valOne : valTwo;
        var minMaxHigh = valTwo < valOne ? valOne : valTwo;
        if (minMaxLow == sldrMin) minMaxLow = 0;
        if (minMaxHigh == sldrMax) minMaxHigh = 999999;
        minMax.value = minMaxLow + "," + minMaxHigh;

        var sLower = (valOne <= valTwo) ? valOne : valTwo;
        var sUpper = (valOne <= valTwo) ? valTwo : valOne;

        //  currency slider
        if(slider.options.name == 'pslider')
        {
          sMin.innerHTML = ta.util.currency.formatCurrency(sLower, crncy);
          var sMaxText = ta.util.currency.formatCurrency(sUpper, crncy);
          if(slider.options.maxAllInclusive && (sUpper == sldrMax)) { sMaxText += "+"; }
          sMax.innerHTML = sMaxText;
        }
        //  rating slider
        else if(slider.options.name == 'rslider')
        {
          if( (sLower+"").length == 1) sLower = sLower + ".0";
          sMin.innerHTML = sLower;
          if( (sUpper+"").length == 1) sUpper = sUpper + ".0";
          sMax.innerHTML = sUpper;

          var pattern = /(^.*)(\d\.\d)(.gif)/;
          updateRatingSrc(sLeft, pattern, true, sLower);
          updateRatingSrc(sRight, pattern, true, sUpper);
        }
        //  star slider
        else if(slider.options.name == 'sslider')
        {
          sMin.innerHTML = sLower;
          sMax.innerHTML = sUpper;

          var pattern = /(^.*)(\d)(.gif)/;
          if(sLower >= 1 && sLower <= 5) //bug 27384
          {
            updateRatingSrc(sLeft, pattern, false, sLower);
          }
          if(sUpper >= 1 && sUpper <= 5) //bug 27384
          {
            updateRatingSrc(sRight, pattern, false, sUpper);
          }
        }
        //  integer slider
        else if (slider.options.name == 'islider')
        {
          sMin.innerHTML = sLower;
          sMax.innerHTML = slider.options.maxAllInclusive && (sUpper == sldrMax) ? 
                           sUpper + "+" :
                           sUpper;
        }
      },
      onComplete: ($defined(window['sliderMoved']) ? sliderMoved : Class.empty),
      steps: sldrSteps-1,
      offset: sOffset,
      round: doRounding,
      name: sName
    });
    slider.setKnobFromValue(sDivOne, selMin, sldrMin, sldrMax, sldrSteps);
    slider.setKnobFromValue(sDivTwo, selMax, sldrMin, sldrMax, sldrSteps);
    slider.sliderOptions = {
      knobs: [sDivOne, sDivTwo],
      min: sldrMin,
      max: sldrMax,
      step: sldrSteps
    };
    elmt.slider = slider;
  }
}
rules['#LEFTNAV .fltr div.dualSliderTest'] = dualSliderRule;
// rules['#HAC_FORM div.dualSliderTest'] = dualSliderRule;

function updateRatingSrc(elem, pattern, isDec, newVal)
{
  var elemMatch = elem.getElement('.lmtImg').src.match(pattern);
  if(elemMatch)
  {
    if( (newVal+"").length == 1 && isDec)
      newVal = newVal + ".0";
    elem.getElement('.lmtImg').src = elemMatch[1] + newVal + elemMatch[3];
  }
}

function sliderStepToValue(min, max, totalSteps, currentStep, round)
{
  if(round)
    return Math.round( currentStep * ( (max - min) / (totalSteps - 1) ) ) + min;
  return currentStep * ( (max - min) / (totalSteps - 1) ) + min;
}

var currencyHash = new Object();
function buildCurrencyHash(ind, min, max, selMin, selMax, steps, maxAllInclusive)
{
  currencyHash[ind] = { 'min' : min, 'max' : max,'selMin' : selMin,'selMax' : selMax, 'steps' : steps, 'maxAllInclusive' : maxAllInclusive};
}

var weeklyCurrencyHash = new Object();
function buildWeeklyCurrencyHash(ind, min, max, selMin, selMax, steps)
{
  weeklyCurrencyHash[ind] = { 'min' : min, 'max' : max,'selMin' : selMin,'selMax' : selMax, 'steps' : steps};
}


// used to submit the page when the user toggles "Original in <language>" or "Automatic Translation" radio buttons
// have to use the onclick event because IE6 doesn't handle onChange properly with radio buttons
//rules['input.submitOnClick[type=radio]'] = function(elmt)
//{
//  elmt.addEvent('click', function() 
//  	{
//  		elmt.form.submit();
//  	});
//}

// Name of the cookie used to track the distance units for "Nearby Locations"
var distanceUnitsCookieName = "TAdistanceCookie";

// rules['#NEARBY .js_miOp']=function(elmt)
// {
//   elmt.addEvent('click', showMi.bindAsEventListener(elmt));
// }

// rules['#NEARBY .js_kmOp']=function(elmt)
// {
//   elmt.addEvent('click', showKm.bindAsEventListener(elmt));
// }

rules['#NEARBY #nearbyDistanceUnits']=function(elmt) 
{
	Cookie.set(distanceUnitsCookieName, $(elmt).value);
}

// Attempt a popUp on window load, on failure show link in lightbox -dkw
rules['#js_tryPop'] = function(tryPop) 
{
  var lnk = tryPop.getElement('a.js_popLink');
  var ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=1";
 
 //link to try
  if(lnk) {
   if(!window.open( lnk.href, lnk.name, ops)) {
    // lightbox message
    if(tryPop.getElement('div.js_lightBoxMsg')) 
     window.addEvent( 'load', function() {  ta.overlays.showInLightbox(tryPop.getElement('div.js_lightBoxMsg').innerHTML) } );
    }
  } 
 }

// Log a Review Link click for reporting
rules['#REVIEW_LINK']=function(elmt) { elmt.addEvent('click', reviewLinkClick.bindAsEventListener(elmt));};
function reviewLinkClick(pid) 
{
  var pid = $('REVIEW_LINK').className.match(/pid(\d+)/) ;
  if (pid && pid[1])
  {
    Cookie.set('NPID', pid[1], {domain: cookieDomain, time:5});
    new Ajax('/ActionRecord?action=review_click').request();
  }
}

rules['#memberFlyout .memberBenefitsPopup'] =function(elmt) {
  elmt.addEvent('click', function(e) { 
    new Event(e).preventDefault();
    showDHTMLPopup('MemberBenefitsPopup', '/MemberBenefitsPopup', true) 
  } );
}

// this must be declared here
var popupConfig = {};

function addNonMember(e, elmt) {
        if (!elmt) elmt = this;
  	popupConfig.clickedElement = elmt;
  	new Event(e).preventDefault();
  	
  	// if the clicked element has a pid then add that to the url
  	var url = '/MemberBenefitsPopup';
  	if (/pid(\d+)/.test(elmt.className)) {
  	  url += '?pid='+RegExp.$1;
  	}
  	
  	showDHTMLPopup('MemberBenefitsPopup', url, true);
}
  
function clickNonMember() {

  setTimeout("window.lightbox.deactivate()", 500);
  
  if (popupConfig.clickedElement == null) return;
  
  var c = popupConfig.clickedElement;
  popupConfig.clickedElement = null;
  
  $$('#TRAVEL_GUIDES_LANDER .clickNonMember').each( function(el, i) {
    el.removeEvent('click', addNonMember);
  });

  $$('#TRAVEL_GUIDES_FLY .clickNonMember').each( function(el, i) {
    el.removeEvent('click', addNonMember);
  });

  if (c.className.match(/\bclickPopDestMOGLink\b/))
    processPopDestMOGLink(null, c);
  else if (c.getTag() == "a")
    window.location= c.getProperty('href');
  else
    c.click();
}

function processPopDestMOGLink(e, elmt) {
  if (!elmt) elmt = this;
  if (!loggedIn)
    addNonMember(e,elmt);
  else 
    processGuideLink(e,elmt);
}

// members only non-member click
rules['#TRAVEL_GUIDES_LANDER .clickNonMember'] = rules['#TRAVEL_GUIDES_FLY .clickNonMember'] = function(elmt) {
  if (elmt.getTag() != 'a' || ! elmt.className.match(/\bskipATag\b/)) {
    elmt.addEvent('click', addNonMember);
  };
}

// members only popular destination guide link click
rules['#TRAVEL_GUIDES_LANDER .clickPopDestMOGLink'] = function(elmt) {
  elmt.addEvent('click', processPopDestMOGLink);
}

// redesign popup logic
// new popup
function DHTMLOverlayRequest(action, responseFunction, errorFunction, formElement)
{
  var sep = "?";
  if (popupConfig.servletUrl.contains(sep)) sep = "&";
  var url = popupConfig.servletUrl+sep+"Action="+action+"&fromServlet=" + pageServlet;
  
  new Ajax(url, {
    onComplete: responseFunction,
    onFailure: errorFunction,
    data: formElement,
    evalScripts: true
  }).request();
}

// Call any automatic popups on page load.  This should be called explicitly.
// To call manually use showDHTMLPopup(popupServlet, popupServletUrl, popupLightbox)
// The variable $popupServlet is decided by the PopupManager.java and shouldn't be set explicitly
function showDHTMLOverlayOnLoad()
{
  if(!doCookieCheck())
  {
    return;
  }
  
  
  if (isOverlayServlet)
  {
    if( overlayOptions == "false" )
    {
      var opts = {};
    }
	else
	{
	  var opts = overlayOptions;
	}
    opts.permanent = true;
    showDHTMLPopup(isOverlayServlet, overlayServletUrl, overlayLightbox, opts);
  }

  if (IS_OVERLAY_DEBUG && typeof makePopupSummary != 'undefined')
  {
    makePopupSummary();
  }
}


// This is the preferred way of showing a Dhtml Popup
// PopupManager.java uses these functions along with some velocty stuff in header_popup.vm
// Note that this means on load popups setup by the servlet! These functions should not be called
// in response to a user action! Use ta/overlays/Factory.js for that!
function showDHTMLPopup(popupServlet, popupServletUrl, popupLightbox, ops, e)
{
    popupConfig.servlet = popupServlet;
    popupConfig.servletUrl = popupServletUrl;
    popupConfig.lightbox = popupLightbox;
    popupConfig.options = ops;
    if (ops && ops.loading && e) {
      e = new Event(e);
      $(ops.loading).show().injectInside(document.body).setStyles({
        left: e.page.x + 20, // to the right of the mouse
        top: e.page.y
      });
    }
    
    DHTMLOverlayRequest("Open", DHTMLOverlayResponse, null, ops ? ops.data : null);
}

var DHTMLOverlayResponse = function(content)
{
  if (popupConfig.options && popupConfig.options.forward && window.popupForward)
  {
    window.location = window.popupForward;
  }

  // make sure we're not showing any overlays
  $$('#FLYOUT').each(function(item, index) {item.remove();});
  window.lightbox.deactivate();
  $$('.overlay').each(function(item, index){ 
    if(item.overlay && item.overlay.hide)
    {
      item.overlay.hide();
    }
    else if(item.hide)
    {
      item.hide();
    }
    else
    {
      item.remove();
    }
  });

  // hide the loading layer    
  if (popupConfig.options && popupConfig.options.loading) $(popupConfig.options.loading).hide();
  
  var overlay;
  var overlayOps = {};
  if (popupConfig.lightbox)
  {
    overlayOps.backdrop = ta.overlays.BACKDROP_ALWAYS;
  }
  if (popupConfig.options && popupConfig.options.delayedPosition) overlayOps.delayedPosition = popupConfig.options.delayedPosition;
  if (popupConfig.options && popupConfig.options.permanent) {
    overlayOps.xOffset = $('PAGE').getLeft() + 100;
    overlayOps.yOffset = $('PAGE').getTop() + 220;
    overlay = new ta.overlays.PermanentOverlay(overlayOps);
  }
  else if (popupConfig.options && popupConfig.options.center) {
    overlay = new ta.overlays.CenteredOverlay(overlayOps);
  }
  else {
    overlayOps.xOffset = $('PAGE').getLeft() + 100;
    overlayOps.yOffset = $('PAGE').getTop() + 220;
    overlay = new ta.overlays.AbsoluteOverlay(overlayOps);
  }
  
  overlay.loadRemoteSuccess(content);
  
  var popupOnHideHandler = function()
  {
    if (typeof dhtmlRedirectLink != 'undefined') {
      window.location = dhtmlRedirectLink;
      return;
    }
    if (this.inner.getElement('.refreshOnClose')) {
      document.location.reload();
      return;
    }
    if (typeof addPopupCloseClass != 'undefined' && !this.inner.getElement('.silentClose')) DHTMLOverlayRequest('Close');
    if (this.inner.getElement('.popupBackOnClose')) {
      if (/(?:^|;)\\s*TAReturnTo=\%1\%([^;]*)/.test(document.cookie)) window.location = decodeURIComponent(RegExp.$1);
      else window.location = "/";
    }
  }
  overlay.addEvent('onHide', popupOnHideHandler);
    
  if (popupConfig.servlet)
  {
      // give us control to apply styles depending on which popup we're showing
      overlay.container.addClass(popupConfig.servlet);
      
      // a subset of the actions defined in DhtmlPopup.java
      // only actions that are triggered by a mouse click
      overlay.inner.getElements('.popupConvert').each(function(item, index){
        item.addEvent('click', function(){ DHTMLOverlayRequest('Convert'); } );
      });
      
      overlay.inner.getElements('.popupDecline').each(function(item, index){
        item.addEvent('click', function(){ DHTMLOverlayRequest('Decline'); } );
      });
      
      overlay.inner.getElements('.dhtmlclose').each(function(item, index){
        item.addEvent('click', function(){ var o = overlay; o.hide(); } );
      });

      var popupSubmitOnEnterFunction = function(e)  
      {
        var charCode = (e.charCode) ? e.charCode :
            ((e.which) ? e.which : e.keyCode)
        if(charCode == 13 || charCode == 10)
        {
          popupSubmitFunction(e);
        }
      }

      var popupSuppressEnterFunction = function(e)  
      {
        var charCode = (e.charCode) ? e.charCode :
            ((e.which) ? e.which : e.keyCode)
        if(charCode == 13 || charCode == 10)
        {
          new Event(e).preventDefault();
        }
      }

      overlay.inner.getElements('.popupSubmitOnEnter').each(function(item, index){
        item.addEvent('keydown', popupSubmitOnEnterFunction );
      });

      var popupSendForgotEmailFunction = function(e) 
      {
        $$('#POPUP_FORM #sendpass').setProperty("value", "true");
        popupSubmitFunction(e);
      }

      overlay.inner.getElements('.popupSendForgotEmail').each(function(item, index){
        item.addEvent('click', popupSendForgotEmailFunction );
      });

      // avoid duplicate submits
      var popupSubmitFunction = function(e) 
      {
        new Event(e).preventDefault();

        overlay.inner.getElements('.popupSubmit').each(function(item, index){
          item.removeEvent('click', popupSubmitFunction);
        });

        overlay.inner.getElements('.popupSendForgotEmail').each(function(item, index){
          item.removeEvent('click', popupSendForgotEmailFunction);
        });

        overlay.inner.getElements('.popupSubmitOnEnter').each(function(item, index){
          item.removeEvent('keydown', popupSubmitOnEnterFunction);
          item.addEvent('keydown', popupSuppressEnterFunction);
        });

        overlay.inner.getElements('.focusClear').each(function(item, index){
          item.setProperty('value', '');
        });

        if (popupConfig.options && popupConfig.options.disableOnHideForSubmit) {
            overlay.removeEvent('onHide', popupOnHideHandler);
        }
        DHTMLOverlayRequest('Submit', DHTMLOverlayResponse, null, overlay.inner.getElementById('POPUP_FORM')); 
      }

      overlay.inner.getElements('.popupSubmit').each(function(item, index){
        item.addEvent('click', popupSubmitFunction );
      });
  }

  if (popupConfig.options && popupConfig.options.callback) 
  {
    popupConfig.options.callback(overlay.container);
  }
  
}

function moveDHTMLPopupToScrollPosition(event)
{
  var popupHeight = this.getSize().size.y;
  
  // popup is bigger than whole window so just don't move it
  if (popupHeight > window.getHeight()) {
    return;
  }
  // keep it centered
  else if (popupConfig.options && popupConfig.options.center) {
    var cs = this.getSize(); 
    this.setStyles({
      left: (window.getWidth() - cs.size.x) / 2 + window.getScrollLeft(),
      top: (window.getHeight() - cs.size.y) / 2 + window.getScrollTop()
    });
  }
  // top offset pushes it out of window
  else if (popupConfig.coords.top + popupHeight > window.getHeight()) {
    var freeSpace = window.getHeight() - popupHeight;
    this.setStyle('top', window.getScrollTop() + (freeSpace/2) + 'px');
  }
  // keep the offset to top defined in base.css
  else {
  	this.setStyle('top', window.getScrollTop() + popupConfig.coords.top + 'px');
  }
}

// Bug 27446
if (window.opener == null && typeof(cpu_enabled) != "undefined")
{   
  var cpu_paused = false;
  var cpu_go = function(e, triggerType) {
    if (cpu_paused && triggerType == 'unload') return;
    if (! doCookieCheck()) { return; }
  
    // Check to make sure it's a link that was clicked,
    // only check if this is the onclick version
    if (triggerType == 'click')
    {
      e = new Event(e || window.event);
      var eTarget = $(e.target);
      var eTargParentA = eTarget.getParent('a');
      if( eTarget.getTag() != 'a' && eTargParentA == null ) { 
        return; 
      }
      
      if( 
          ( eTarget.className && eTarget.className.match(/\b(js_pop)/) )
          || ( eTargParentA != null && eTargParentA.className && eTargParentA.className.match(/\b(js_pop)/) ) 
          || eTarget.getParent('#CRUISE_CRITIC_REVIEWS_FORM')
        )
      {
        return;
      }
                
      if( eTarget.onclick && eTarget.onclick != null && eTarget.onclick.toString().match(/window\.open/g))
      {
        return;               
      }
    }
  
    var alreadyPopped = Cookie.getRaw('CommercePopunder');
    if (alreadyPopped)
    {
      // We want to suppress unless the trigger type is a click and we have a SuppressUnload cookie
      if (alreadyPopped != 'SuppressUnload' || triggerType != 'click')
      {
        return;
      }
    }

    if(blockPU)
    {
      return;
    }
        
    //don't spawn MPU if coming from Flights servlet
    if(IS_MPU_ENABLED && "Flights" == pageServlet)
    {
      return;
    }
         

    // check if commerce click happened
    var csv = Cookie.getRaw('TASession');
    if (csv && /\*PD(\d+)\.(\d+)/.test(csv) && /\*CC\.(\d+)/.test(csv) && !IS_MPU_ENABLED)
    {
      new Ajax("/ActionRecord?action=AbortedOpen_expediaOrbitzCommerceClick").request();
      return;
    }

    //Double check for data in cookie (user may have deleted cookies mid-session)
    // bug 28579
    if((!csv || !(/\*LL\.(\d+)/.test(csv))) && !IS_MPU_ENABLED) // if no cookie or no commerce-valid loc data in cookie
    {
      new Ajax("/ActionRecord?action=AbortedOpen_noCookieData").request();                
      return;
    }

    var w = 1;
    var h = 1;
    if (window.ie7) {w = 250; h = 100;}
    if (window.webkit) { w = 85; h = 100;}
    var nX = (window.screenLeft || window.screenX);
    var nY = (window.screenTop || window.screenY);
    
    if (typeof(nX) == "undefined") { // full screen
      nX = 0;
      nY = 0;
      var x = nX + window.screen.availWidth - w; // bottom-right corner of window
      var y = nY + window.screen.availHeight - h;
    }
    else{
      var x = nX + window.getWidth() - w; // bottom-right corner of window
      var y = nY + window.getHeight() - h;
    }
    
    if(window.ie7)
    {
      y = y - 95;
      x = x + 13;
    }
    else if(window.webkit)
    { 
      y = y - 16;
    }
    else
    {
      x = 5000;
      y = 5000;
    }
        
    var cookieDuration = 5;
    if(IS_MPU_ENABLED)
    {
      cookieDuration = 3;
    }
        
    Cookie.set("CommercePopunder", "SuppressAll", {domain: cookieDomain, duration: cookieDuration});

    var backupParam = "";
    if(overlayBackupLoc)
    {
      backupParam = "&backupLoc=" + overlayBackupLoc;
    }

    var encoded_cpu_url = escape(window.location.href);
    var cpu_uri = 'http://'+window.location.hostname+'/CommercePopunderEnhanced?mainWindowReturnTo=' + encoded_cpu_url + '&fromServlet=' + pageServlet + backupParam;
    var width=790;
    var height=660;
    if(IS_MPU_ENABLED && "CheapFlights" != pageServlet)
    {
      var rand = $random(1,2);
      cpu_uri = cpu_uri + "&flights="+rand;
      width = 715;
      if (rand=="1")
      {
        height = 450;
      } else {
        height = 500;
      }
    }
    
    if (triggerType == 'click')
    {
        cpu_uri = cpu_uri + "&onclick=1"
    }
        
    // record attempt to launch popunder
    cpu_win = window.open(cpu_uri, "CommercePopunder", "toolbar=1,location=1,directories=1,status=1,menubar=1,resizable=1,copyhistory=1,scrollbars=1,width="+w+",height="+h+",left="+x+",top="+y);
    if (cpu_win != null) {
      cpu_win.opener = self;
      cpu_win.blur();
      window.focus();
      cpu_win.moveTo(nX + (window.getWidth() - width) / 2, nY + (window.getHeight() - height) / 2);
      cpu_win.resizeTo(width,height);
    }
    else
    {
      // Can't rely on an Ajax during an unload
      new Asset.image('/ActionRecord?action=fail_commercePopunderBlocked_' + triggerType);

      // If this was an unload CPU...
      if (triggerType == 'unload')
      {
        // Then suppress future unloads (allowing clicks)
        Cookie.set("CommercePopunder", "SuppressUnload", {domain: cookieDomain, duration: cookieDuration});
      }
    }
        
  } // end of cpu_go function 

  var cpu_resume = function() {window.cpu_paused = false;}
  
  // runs when user clicks in page
  var cpu_pause = function(e) {
    e = new Event(e || window.event);
    var eTarget = $(e.target);
    
    // if the user clicks within the site, prevent popunder from showing before next page to load
    // logic is user is navigating within the site or clicking commerce to leave the site
    if (!cpu_paused && (eTarget.getTag() == 'a' || eTarget.getParent('a') != null)) {
        window.cpu_paused = true;
        cpu_resume.delay(1000);
    }
  }

  // runs on load
  var cpu_run = function() {
    // Add handlers for unload
    window.addEvent('beforeunload', function (e) { cpu_go (e, 'unload'); } );
    document.addEvent('click', cpu_pause);

    // Add handler for onclick
    document.addEvent('click', function (e) { cpu_go (e, 'click'); } );
  }
}

function resizeToWindow(event)
{
  var pageSize = Math.max( window.getHeight(), window.getScrollHeight() );
  this.setStyle('height', pageSize + 'px');
}

function showToggleBlock(toggleClass)
{
  // only hide all the toggle blocks besides toggleClass
  $$('.js_toggleBlocks .js_toggleBlock').each( function(el, i) {
    el.hide();
	if (el.hasClass(toggleClass))
	{
	  el.show();
	}
  });
}

linkMap['js_reopenDhtmlPopup']=function(elmt, e) {
    new Event(e).preventDefault();
    showDHTMLPopup(popupConfig.servlet, popupConfig.servletUrl, popupConfig.lightbox);
} 

linkMap['js_toggleBlockTrigger'] =function (elmt, e) { 
  var toggleClass = elmt.getProperty("class");
  toggleClass.replace(/toggleBlock[0-9]+/g, function(match){ toggleClass = match; });
  showToggleBlock(toggleClass);
};

rules['#SWAPBLOCK .js_toggleBlock']=function(elmt) {
  // the default toggleBlock
  if (elmt.hasClass("js_toggleBlockSelected"))
  {
    var toggleClass = elmt.getProperty("class");
    toggleClass.replace(/toggleBlock[0-9]+/g, function(match){ toggleClass = match; });
    showToggleBlock(toggleClass);
  }
}

function clearPopupForm()
{
  // clear password
  $$('#POPUP_FORM #pass').setProperty("value", "");
  
  // clear errors
  $$('#DHTMLPOPUP .error-message').empty();
}

// only used in DHTML pop-ups
linkMap['js_clearPopupForm'] =function (elmt, e) { 
  clearPopupForm();
};

function setPopupFormAction(val)
{
  $$('#POPUP_FORM #action').setProperty("value", val);
}

// only used in DHTML pop-ups
linkMap['js_popupFormAction1'] =function (elmt, e) { 
  setPopupFormAction("1");
};
linkMap['js_popupFormAction2'] =function (elmt, e) { 
  setPopupFormAction("2");
};

function callPopupSendPasswordEmail(elmt)
{
  alert("callPopupSendPasswordEmail");
  $$('#POPUP_FORM #sendpass').setProperty("value", "true");
  popupSubmitFunction(elmt);
}

// only used in DHTML pop-ups
ajaxRules['.js_popupForgot']=function(elmt) {
  elmt.addEvent("click", callPopupSendPasswordEmail);
}

// MOVE - only used in Help Center
rules['#HELP_CENTER dl.js_toggleset'] = function(elmt) {
  var initTab = 0;
  var ts = new ToggleSet(elmt.getElements('dt'), elmt.getElements('dd'));
  if (window.location.hash && (hash = window.location.hash.match(/c(\d+)/))) ts.display(parseInt(hash[1]));
}

// XXX - used for pool testing of amenity icons
// This should be removed in the not so distant future
// Greg Belote  19aug08

var amenityFiredHoverEvent = false;

function recordAmenityIconHover ()
{
    // send an AJAX request that will log the hover
    new Ajax("/ActionRecord?action=amenityIconHover").request();

    // note that we've recorded one hover for this page view
    amenityFiredHoverEvent = true;
}

//rules['#redesignAmenity .amenities img'] = function(icon) {
//    icon.addEvent('mouseenter', function(e) {
//        // if we haven't already recorded a hover for this page view...
//        if (amenityFiredHoverEvent == false)
//        {
//            // set a timer at 750ms to record the user hovering
//            icon.hoverTimeout = setTimeout ("recordAmenityIconHover()", 750);
//        }
//    });
//
//    icon.addEvent('mouseleave', function(e) {
//        // stop the timeout
//        clearTimeout (icon.hoverTimeout);
//        
//        // remove any references to it
//        icon.hoverTimeout = null;
//    });
//}

var propertyTypeClicked = false;

//rules['#PROPERTY_TYPE .flyout a'] = function(questionMark)
//{
//    questionMark.addEvent('click', function(e) {
//        if (propertyTypeClicked == false)
//        {
//            new Ajax("/ActionRecord?action=propertyTypeClicked").request();
//            propertyTypeClicked = true;
//        }
//    });
//}

var mtHelpHovered = false;
rules['#ACCOM_DETAIL div.translation li.flyoutB a, #SHOW_USER_REVIEW div.translation li.flyoutB a'] = function(questionMark)
{
	questionMark.addEvent('mouseenter', function(e) {
		if (mtHelpHovered == false)
		{
			new Ajax("/ActionRecord?action=MTQuestionmarkHover").request();
			mtHelpHovered = true;
		}
	});
}

rules['#HAC_FORM .js_hacRadio'] = function(elmt) {
  if(!elmt.checked)
  {
    elmt.addEvent("click", function(e) {
    	var aelmt = $('cat' + elmt.value + 'url');
    	if(aelmt)
    	{
          window.location = aelmt.href;
    	}
    });
  }
}

ajaxRules['#HotelDateSearch_CR ul.siteLst'] = function(elmt) {
  if(typeof vendors != "undefined")
  {
    vendors.each(function(vendor) {
      var bn_checked = "";
      if($(vendor.name).checked)
      {
        bn_checked = ' checked="checked"';
      }
      new Element('li').setHTML(
        '<input id="bn' + vendor.name + '" onchange="$(this.id.substring(2, this.id.length)).checked = this.checked" class="chk" name="' + vendor.name + '" type="checkbox"' + bn_checked + '/>' +
        '<label for="bn' + vendor.name + '">' + vendor.vendorName + '</label>'
      ).injectInside(elmt);
    }, elmt);
  }  
}

ajaxRules['#FLAGS_FLY'] = function(elmt) {
  elmt.getElements('a').each(function(e) {
      var footerFlag = $(e.id.replace(/_fly/, ""));
      if (footerFlag) {
        e.href = footerFlag.href;
      }
    });
}

rules['#PHPROMO'] = function(elmt)
{
      // Make AJAX call to get content.
      new Ajax('/' + elmt.getText(), {
        onComplete: function(txt, xml) { 
            elmt.empty();
            elmt.innerHTML = txt;
            window.behavior.apply(elmt);
            elmt.style.display = 'block';
          } 
        }).request();
}

rules['#PHPROMOX'] = function(elmt)
{
      var url = elmt.getText();
      if (url)
      {
        url = '/' + url;
        elmt.empty();
        replaceContent(null, elmt, url);
        elmt.style.display = '';
      }
}

// only needed if savesEnable
var showLastSavesRD = function()
{
  var node = $('LAST_SAVES_PLACEHOLDER');
  if (typeof savesIncluded != 'undefined') {
    if (!node) return renderLastSaves(SAVES_RECENT_SAVES);
    else {
      node.innerHTML = renderLastSaves(SAVES_RECENT_SAVES);
      flyout.positionCorners();
    }
  }
  else if (!node) {
    new Asset.javascript(savesJS);
    showLastSavesRD.delay(125);
    return new Element('div', {id: 'LAST_SAVES_PLACEHOLDER'});;
  }
}

var showSavesWidget = function(anchor, entityType, entityId, options, xOff, yOff, fromLander) {
  if (typeof savesIncluded != 'undefined') {
    showSavesWidget2(anchor, entityType, entityId, options, xOff, yOff, fromLander);
  }
  else {
    new Asset.javascript(savesJS);
    showSavesWidget.delay(125, null, [anchor, entityType, entityId, options, xOff, yOff, fromLander]);
  }
}

var savesInlineLoginOnClickHandler = function(loggedInFn, loginFn, clickedElement) {
  if (typeof savesIncluded != 'undefined') {
    savesInlineLoginOnClickHandler2(loggedInFn, loginFn, clickedElement);
  }  
  else {
    new Asset.javascript(savesJS);
    savesInlineLoginOnClickHandler.delay(125, null, [loggedInFn, loginFn, clickedElement]);
  }
}

var doSavesPostLoginPopup = function() {
  if (typeof savesIncluded != 'undefined') {
    handleSavesPostLoginPopup();
  }  
  else {
    new Asset.javascript(savesJS);
    doSavesPostLoginPopup.delay(125);
  }
}

linkMap['js_trackDirPop'] = function(elmt, e) {
  if (actionToRecord = elmt.className.match(/ar_(\w+)/)) {
    actionToRecord = actionToRecord[1];
    new Ajax("/ActionRecord?action=" + actionToRecord).request();
  }
}

function expandIframe(id,w,h){
  if(document.getElementById(id)){
    document.getElementById(id).width=w;
    document.getElementById(id).height=h;
  }
}

function collapseAd(id)
{
  var frameE = $(id);
  if (frameE == null) return;
  var e = frameE.getParent('.adServer');
  if (e == null) return;
  e.addClass('taEmpty');
}

// might not need this
function expandAd(id, h)
{
  var frameE = $(id);
  if (frameE == null) return;
  var e = frameE.getParent('.ad');
  if (e == null) return;
  e.setStyle('height', h + 'px');
}

function injectAds()
{
  var ads = $$(".adServer .details");
  if (ads.length == 0) return;
  var url = "adp/adp.html";
  if (adpHtml) {
    url = adpHtml;
  }
  var domainParam = window.documentDomainChanged ? 'dd+' : '';
  for(var i=0; i<ads.length; i++)
  {
    var el = document.createElement("iframe");
    var id = ads[i].id + '_iframe';
    el.setAttribute('id', id);
    el.setAttribute('name', id);
    el.setAttribute('height', 0); 
    el.setAttribute('width', 0) ; 
    el.setAttribute('scrolling', 'no');
    el.setAttribute('border', '0');
    el.setAttribute('marginheight', '0');
    el.setAttribute('marginwidth', '0');
    el.setAttribute('frameBorder', '0');
    el.setAttribute('src', 'http://' + document.location.host + "/" + url + "#" + domainParam + eval(ads[i].id));
    ads[i].getParent().appendChild(el);
  }
}

window.addEvent('load', function() {
    injectAds.delay(10);  // Be sure all else is done before loading ads.
} );

/*
 * Replace the contents of an element via XHR
 */
var replaceContent = function(/* Event */ event,
                              /* HTMLElement */ element,
                              /* String */ href)
{
    //  local copies for our callbacks
    var myEvent = event;
    var myElement = element;
    
    //  if we haven't already sent a request (filters out double clicks)
    if (!myElement.getElement('.progresstab'))
    {
        //  add an 'xhr' request parameter to indicate what this is for
        var myHref = href + (href.indexOf('?') == -1 ? '?' : '&') + 'xhr=true';
        
        // Set content to placeholder graphic
        var imgCntr = new Element('div', { 'class':'progresstab' } );
        imgCntr.injectInside(myElement);
    
        // Make AJAX call to get content.
        new Ajax(myHref,
        {
            onComplete: function(txt, xml)
            {
                myElement.empty();
                myElement.innerHTML = txt;
                
                // apply any defined behavior
                window.behavior.apply(myElement);
                
                //  notify anyone listening we are done
                myElement.fireEvent('onContentReplaced', [myEvent, myElement]);
            },
            evalScripts:true
        }).request();
    }
};

linkMap['js_replaceContent'] = function(elmt, e)
{
    //  get the target element's id
    var match = elmt.className.match(/id_([^\s"]*)?/);
    if (!match || match.length != 2) throw new Error("Links with behavior js_replaceContent must also have a style class id_??? to indicate which element's contents should be replaced");
    match = match[1];
    
    //  get the target element
    var element = $(match);
    
    var href = elmt.href;
    if (href) 
    {
        //  replace the contents of the element via XHR
        replaceContent(e, element, href);
    }
    e.stop();
};

rules['#TAB_CONTAINER'] = function(elmt, e)
{
    //  grab the initially selected tab and its index
    var selectedTab = elmt.getElements('.current');
    if (selectedTab.length != 1)
    {
        throw new Error("You must have exactly one tab with a class of 'current'.");
    }
    else
    {
        selectedTab = selectedTab[0];
    }
    var selectedTabIndex = Number(selectedTab.className.match(/tab(\d+)/)[1]);
    
    //  if we are on the home page, we allow the currently selected tab to
    //  be defined by a 't' has param
    if ($('HOMEPAGE') && window.location.hash)
    {
        var hash = window.location.hash.match(/t(\d)/);
        if (hash && hash.length > 0)
        {
            selectedTabIndex = parseInt(hash[1]);
            selectedTab.removeClass('current');
            elmt.getElements('.tab')[selectedTabIndex].addClass('current');
        }
    }
    
    //  grab references to all tabs and tab content nodes
    var tabs = elmt.getElements('.tabContainerHead .tab');
    if (tabs.length == 0) return;
    var elmts = elmt.getElements('.tabContainerBody .tabContent')

    //  create a new tab set    
    new TabSet(tabs, elmts,
    {
        show:selectedTabIndex,
        onActive:function(event, toggler, element)
        {
            var xhr = toggler.hasClass('xhr');   
            
            //  if we are using XHR to populate the tab content and we have not already done so
            if (xhr && !toggler.hasClass('xhrComplete'))
            {
                // grab the href
                var href = toggler.getElement('a').href;
                if (href) 
                {
                    //  replace the contents of the tab element via XHR
                    var myToggler = toggler;
                    element.addEvent('onContentReplaced', function(){myToggler.addClass('xhrComplete');});
                    replaceContent(event, element, href);
                }
            }
            
            //  if we should record the user action
            if (toggler.hasClass('recordAction'))
            {
                new Ajax('/ActionRecord?action=' + toggler.id).request();
            }
            
            //  add a class to mark this as the current tab
            toggler.addClass('current');
            
            //  if we are using XHR then stop the event so we do not navigate the entire page
            if (xhr && event) event.stop();
        },
        onBackground:function(event, toggler, element)
        {
            toggler.removeClass('current');
        }
    });
};

//close the lightbox (for 'account has been deleted' lightbox)
rules['#LIGHTBOX_CLOSE'] = function(elmt) {
  elmt.addEvent("click", function(e) {
    var lb = ta.retrieve('overlays.current');
    if (lb) lb.hide();
  });
};

//Expand click area for offers in BBDN box on Tourism Servlet
//rules['#BBD tr.offerRow'] = function(elmt) {
//  elmt.addEvent('click', function(e) {
//    var event = new Event(e);
//    event.preventDefault();
//    offerLink = elmt.getElement('a');
//    popCommFN = linkMap['js_popComm'];
//    popCommFN(offerLink,e);
//  });
//} 

//  open the vacation rentals calculator in a lightbox
function vrCalculator (elmt, e)
{
    var event = e != null ? new Event(e) : null;

    if (elmt && /pid(\d+)/.test(elmt.className)) Cookie.set('NPID', RegExp.$1, {domain: cookieDomain, time:5});
    
//  if this is the home page promo
    var args = '';
    var vrCalcForm = $('homePageVRCalcPromo');
    if (!vrCalcForm)
    {
        // try looking for the div from the VR overview page
        vrCalcForm = $('calculatorOverviewPromo');
    }
    if (!vrCalcForm)
    {
        // try looking for the div from the VR overview page
        vrCalcForm = $('calculatorLanderPromo');
    }
    if (vrCalcForm && $('vrCalculatorForm'))
    {
        //  if the user did not provide a geo name, clear the tip text
        var geoNameField = vrCalcForm.getElement('#geoName');
        if (geoNameField.hasClass('focusClear'))
        {
            geoNameField.value = '';
            geoNameField.removeClass('focusClear');
        }
        
        //  grab our args
        args = '&geoName=' + vrCalcForm.getElement('#geoName').value;
    }
    
    //  open an ajax lightbox and when active focus on the first input element
    new ta.overlays.CenteredOverlay({
      backdrop: ta.overlays.BACKDROP_ALWAYS,
      onReady: function() { this.inner.getElement('input').focus();}
    }).loadRemoteContent('/VacationRentalCalculator?restart=true' + args);
    if (event) event.stop();
    return false;
};

//  update the vacation rentals calculator with results
linkMap['js_submitVRCalculator'] = function(elmt, e)
{
    var event = e != null ? new Event(e) : null;
    
    //  get the calculator div and form
    var div = $('vrCalculator');
    var form = div.getElement('form');
    
    //  hide any server error messages since they are trying again
    var serverError = div.getElement('.serverErrorMsg');
    if (serverError)
    {
        serverError.style.display = 'none';
    }
    
    //  make sure they selected a valid destination
    if (form.geoName.value == '')
    {
        div.getElement('.errorMsg').style.display = '';
    }
    //  else assemble a url from the form and replace the div's contents with the resulting
    //  content
    else
    {
        var url = form.action + '?' + form.toQueryString();
        replaceContent(e, div.getParent(), url);
    }
    if (event) event.stop();
    return false;
};
//pulled from lib/fbconnect.js for inclusion in tripadvisor.js
/*
 * Copyright (c) 2008 Aza Raskin (http://azarask.in/blog)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var SocialHistory = function() {

  // associative array of sites we're interested in
  // { site: [variations1, variation2...], site: [ ... }
  var sites = {
    "f": ["http://www.facebook.com/", "https://login.facebook.com/login.php", "http://www.facebook.com/home.php"]
    //"f": ["http://www.facebook.com/"]
  };

  var visited = {};

  function getStyle(el, scopeDoc,styleProp) {
    if (el.currentStyle)
      var y = el.currentStyle[styleProp];
    else if (window.getComputedStyle)
      var y = scopeDoc.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
    return y;
  }
  
  function remove( el ) {
    el.parentNode.removeChild( el );
  }
  
  // Code inspired by:
  // bindzus.wordpress.com/2007/12/24/adding-dynamic-contents-to-iframes
  function createIframe() {
    var iframe = document.createElement("iframe");
    iframe.style.position   = "absolute";
    iframe.style.visibility = "hidden";

    document.body.appendChild(iframe);

    // Firefox, Opera
    if (iframe.contentDocument)   iframe.doc = iframe.contentDocument;
    // Internet Explorer
    else if(iframe.contentWindow) iframe.doc = iframe.contentWindow.document;

    // Magic: Force creation of the body (which is null by default in IE).
    // Also force the styles of visited/not-visted links.
    iframe.doc.open();
  	iframe.doc.write('<style>');
  	iframe.doc.write("a{color: #000000; display:none;}");  	
  	iframe.doc.write("a:visited {color: #FF0000; display:inline;}");  	
  	iframe.doc.write('</style>');
    iframe.doc.close();
    
    // Return the iframe: iframe.doc contains the iframe.
    return iframe;
  }  

  // create the iframe, insert links, check them, then remove the iframe
  var iframe = createIframe();

  function embedLinkInIframe( href, text ) {
    var a = iframe.doc.createElement("a");
    a.href      = href;
    a.innerHTML = site;
    iframe.doc.body.appendChild( a );
  }

  // add links to each site
  for (var site in sites) {
    var urls = sites[site];
    for( var i=0; i<urls.length; i++ ) {
      // You have to create elements in the scope of the iframe for IE.
      embedLinkInIframe( urls[i], site );
      
      // Automatically try variations of the URLS with and without the "www"
      if( urls[i].match(/www\./) ){
        var sansWWW = urls[i].replace(/www\./, "");
        embedLinkInIframe( sansWWW, site );
      } else {
        // 2 = 1 for length of string + 1 for slice offset
        var httpLen = urls[i].indexOf("//") + 2;
        var withWWW = urls[i].substring(0, httpLen ) + "www." + urls[i].substring( httpLen );
        embedLinkInIframe( withWWW, site );
      }
    }
  }
    
  var links = iframe.doc.body.childNodes;
  for( var i=0; i<links.length; i++) {
    // Handle both Firefox/Safari, and IE (respectively)
    var displayValue = getStyle(links[i], iframe.doc, "display");
    var didVisit = displayValue != "none";
      
    if( didVisit ){
      visited[ links[i].innerHTML ] = true;
    }
  }

  // done; remove the iframe
  remove( iframe );
  
  return new (function(){
    var usedSites = [];
    for( var site in visited ){
      usedSites.push( site );
    }
    
    // Return an array of visited sites.
    this.visitedSites = function() {
      return usedSites;
    }
    
    // Return true/false. If we didn't check the site, return -1.
    this.doesVisit = function( site ) {
      if( typeof( sites[site] ) == "undefined" )
        return -1;
      return typeof( visited[site] ) != "undefined";
    }
    
    var checkedSites = [];
    for( var site in sites ){
      checkedSites.push( site );
    }
    // Return a list of the sites checked.
    this.checkedSites = function(){
      return checkedSites;
    }
  })();
}

/*
Looks to see if the browser has been to facebook and adjusts the
hidden field appropriately.
*/
function sniffFacebook() {
  var been = false;
  try {
    var sl = SocialHistory();
    var list = sl.visitedSites();
    if (list[0] == 'f') {
      been = true;
    }
  }
  catch(e) {
  }
  return been;
}
/*
Script: trcore.js
    Definitions used by legacy components on fully redesigned pages. Put things here instead
    of tripcompat.js for things used by components on both tweaked and fully redesigned pages
*/

function registerOnLoad(fn) {
  window.addEvent('load', fn);
}
function registerOnUnload(fn) {
  window.addEvent('unload', fn);
}

//func: getScrollOffset
function getScrollOffset()
{
  return [ window.getScrollLeft(), window.getScrollTop() ];
}

function isIn(eventPos, ele, bounds)
{
  var off = getScrollOffset();

  ele = $(ele);
  var pos = findPos(ele);
  // calculate element bounts
  var l = pos[0] - off[0];
  var t = pos[1] - off[1];
  var r = l + ele.clientWidth;
  var b = t + ele.clientHeight;

  if (bounds) {
    if (typeof(bounds) == "number") {
      l = l - bounds;
      t = t - bounds;
      r = r + bounds;
      b = b + bounds;
    }
    else if (bounds instanceof Array && (bounds.length == 2 || bounds.length == 4)) {
      t = t - bounds[0];
      r = r + bounds[1];
      l = l - (bounds.length == 4 ? bounds[3] : bounds[1]);
      b = b + (bounds.length == 4 ? bounds[2] : bounds[0]);
    }
  }

  return (eventPos[0] >= l && eventPos[0] <= r && eventPos[1] >= t && eventPos[1] <= b);
}

function setDetails(event, title, category, space) {
  new Event(event).stop();
  $('title').value = title;
  $('category').value = category;
  $('space').value = space;
  $('newPage').submit();
}

function setOneTimeCookie(key,value)
{
  Cookie.set(key, value, {domain: cookieDomain, time: 5});
  return true;
}

/**
 * Element.getElement() is slow on IE. So, mimic the most common use case with simpler, faster code.
 */
var getChildByClass = function(node,className,tagName) {
  if (!className || className.length == 0) {
    if (tagName == node.tagName)
      return node;
  }
  else if (node.className && (!tagName || tagName == node.tagName)) {
    var pos = -1;
    do
    {
      pos = node.className.indexOf(className,pos+1);
      if (pos >= 0 && (pos == 0 || node.className.charCodeAt(pos-1) == 32) && (pos+className.length == node.className.length || node.className.charCodeAt(pos+className.length) == 32))
        return node;
    } while (pos >= 0);
  }
  for (var i = 0; i < node.childNodes.length; i++) {
    var result = getChildByClass(node.childNodes[i],className,tagName);
    if (result)
      return result;
  }
  return null;
}
