//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.mozInnerScreenX!=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;
  }
});Array.extend({
  /*
  Property: getRandomIndex
    returns a random index of the Array
  */

  getRandomIndex: function(){
    return $random(0, this.length - 1);
  }
});
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 {Element} [elmt] 'this'
 * @param {mixed} [arg] an additional argument to pass to the function
 * @returns {boolean} The return value of the function.
 */
ta.call = function(funcName, evnt, elmt, arg) {
  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" && !IS_DEBUG) return fn(e, elmt, arg);
  }
  catch (_ex) {
    ta.util.error.record(_ex, 'ta.call', evnt, {'funcName': funcName});
  }
  if (IS_DEBUG && $defined(fn) && $type(fn) == "function") return fn(e, elmt, arg);

  // 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;
};

// flag signifying that an event has already fired
ta.EVENT_PASSED = function (args) {
  this.args = args;
};

/**
 * Listen for an event to happen.  If the event is non-recurring and has already happened, 
 * you are notified immediately.
 * @param {String} name event name
 * @param {mixed} f callback
 */
ta.on = function(name, f) {
  var v = ta.retrieve(name);
  if (v instanceof ta.EVENT_PASSED) {
    // too late! you missed it!  
    // using setTimeout in case you were expecting your other code to run first
    setTimeout(function () { f.attempt(v.args) }, 0);
  } else {
    ta.keep(name, f);
  }
}

/**
 * Notify listeners that an event which only happens once per page has taken place.
 * Note: arguments will be held until page unload!
 * @param {String} name event name
 */
ta.fireNonRecurringEvent = function(name) {
  var listeners = ta.retrieve(name);
  var args = $A(arguments);
  args.shift();
  
  ta.store(name, new ta.EVENT_PASSED(args));
  if (listeners instanceof Array) {
    listeners.each(function (x) { x.attempt(args) });
  }
}

ta.fireEvent = function()
{
  var args = $A(arguments);
  var event = args.shift();
  var listeners = ta.retrieve(event);
  if (listeners)
  {
    listeners.each(function(callback){
      callback.attempt(args);
    });
  }
};


/*
  On Ready and Load Queues
*/

/**
 * simple class that represents a queue of functions in priority order
 * lower priority functions are executed first
 * functions are executed once only
 * default priority is 100 + current list length
 * @param {String} evtName window event which triggers running functions
 */
ta.OneTimeFunctionQueue = function(evtName) {
  // private
  var _queue = [];
  var _runNow = false;

  var _execFn = function(fn) {
    try {
      fn();
    } catch (_ex) {
      try {
        ta.util.error.record(_ex, 'ta.OneTimeFunctionQueue' + evtName);
      } catch (ignore) {
        // If for some reason recording an error fails we still
        // want to run the rest of the functions
        // swallow the error since there is no good way to report it
      }
    }
  };

  window.addEvent(evtName, function() {
    // turn off queueing, any new functions should run immediately
    _runNow = true;
    // execute in priority order
    _queue.sort(function(a,b) { return a.priority - b.priority; });
    for (var i = 0; i < _queue.length; ++i) {
      _execFn(_queue[i].func);
    }
    // clear queue to ensure functions aren't run again
    _queue = [];
  });

  // public
  this.addToQueue = function(fn, priority) {
    if (_runNow) {
      _execFn(fn);
    } else {
      priority = priority || (100 + _queue.length);
      _queue.push({func: fn, priority: priority});
    }
  };
};


/**
 * @private
 */
ta._readyQ = new ta.OneTimeFunctionQueue('domready');

/**
 * Add a function to be run when the page is ready.
 * @param {function} fn function to run
 * @param {integer} priority optional argument. lower priority functions are executed first
 */
ta.queueForReady = function(fn, priority) {
  ta._readyQ.addToQueue(fn, priority);
};


/**
 * @private
 */
ta._loadQ = new ta.OneTimeFunctionQueue('load');

/**
 * Add a function to be run when the page loads.
 * @param {function} fn function to run
 * @param {integer} priority optional argument. lower priority functions are executed first
 */
ta.queueForLoad = function(fn, priority) {
  ta._loadQ.addToQueue(fn, priority);
};


/*

  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;
}

/** Convenience methods from Prototype **/

ta.invoke = function(enumerable, fn) { enumerable.each(function(e) { e[fn]() }) };
ta.selectInvoke = function(selector, fn) { ta.invoke($$(selector),fn) };

/** @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: "/"});
}

ta.util.cookie.getPool = function() {
	return Cookie.get('ServerPool');	
}/**
 * 
 * @author jnowell
 * @since  2009.06.26
 */
ta.util.ASDF = {
  asdf: function(str, pid) {
    if (pid) {
      ta.util.cookie.setPIDCookie(pid);
    }
    window.location = asdf(str);
  },

  asdfPopup: function(str, pid) {
    if (pid) {
      ta.util.cookie.setPIDCookie(pid);
    }
    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);
  },

  asdfDcd: function(str) {
    return asdf(str);
  }
};

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 OBF = {
	    "":["&","=","p","6","?","H","%","B",".com","k","9",".html","n","M","r","www.","h","b","t","a","0","/","d","O","j","http://","_","L","i","f","1","e","-","2",".","N","m","A","l","4","R","C","y","S","o","+","7","I","3","c","5","u",0,"T","v","s","w","8","P",0,"g",0],
	    "q":[0,"__3F__",0,"Photos",0,"https://",".edu","*","Y",">",0,0,0,0,0,0,"`","__2D__","X","<","slot",0,"ShowUrl","Owners",0,"[","q",0,"MemberProfile",0,"ShowUserReviews","\"","Hotel",0,0,"Expedia","Vacation","Discount",0,"UserReview","Thumbnail",0,"__2F__","Inspiration","V","Map",":","@",0,"F","help",0,0,"Rental",0,"Picture",0,0,0,"hotels",0,"ftp://"],
	    "x":[0,0,"J",0,0,"Z",0,0,0,";",0,"Text",0,"(","x","GenericAds","U",0,"careers",0,0,0,"D",0,"members","Search",0,0,0,"Post",0,0,0,"Q",0,"$",0,"K",0,"W",0,"Reviews",0,",","__2E__",0,0,0,0,0,0,0,"{","}",0,"Cheap",")",0,0,0,"#",".org"],
	    "z":[0,"Hotels",0,0,"Icon",0,0,0,0,".net",0,0,"z",0,0,"pages",0,"geo",0,0,0,"cnt","~",0,0,"]","|",0,"tripadvisor","Images","BookingBuddy",0,"Commerce",0,0,"partnerKey",0,"area",0,"Deals","from","\\",0,"urlKey",0,"\'",0,"WeatherUnderground",0,"MemberSign","Maps",0,"matchID","Packages","E","Amenities","Travel",".htm",0,"!","^","G"]
	  };
	  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, args, bind) {
  if (ta.has('pending.'+id)) ta.util.pending.cancel(id);
  ta.store('pending.'+id, {timer: fn.delay(limit, bind, args), '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.callback.'+id))
        {
          ta.retrieve('pending.callback.'+id)();
        }
        else 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 && !ta.has('pending.ignore.'+id))
    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);
};

/**
 * Waits a set amount of time and then runs the function unless aborted.
 * @param {String} id lock name
 * @param {Function} fn function to run
 * @param {integer} delay time to wait in ms
 * @param {Object} bind object to bind the function to
 * @param {Array} args arguments to pass to the function
 */
ta.util.pending.wait = function(id, fn, delay, bind, args)
{
  ta.store('pending.'+id, {timer: fn.delay(delay, bind, args)});
};


ta.util.pending.waitForElement = function(id, elmtId, callback)
{
  if (ta.has('pending.'+id)) return;
  var elmt = $(elmtId);
  if (elmt) {
    ta.util.pending.cancel('pending.'+id);
    callback(elmt);
  }
  else ta.store('pending.'+id, {timer: ta.util.pending.waitForElement.delay(2, null, [id, elmtId, callback])});
}
/**
  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);
  if (typeof(gmapDomain) == "undefined") { gmapDomain = 'maps.google.com'; }
  if (typeof(gLang) == 'undefined' || !gLang) { gLang = ''; }
  new Asset.javascript('http://' + gmapDomain + '/maps?file=api&v=2.118&client=' + gClient + '&sensor=false&c&async=2&callback=ta.util.load.GMapsCallback' + gLang);
  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?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() {}});
                            });
};
/*
Script: LazyLoad.js
  Contains <LazyLoad>
*/
var LazyLoad = new Class({

  /* additional options */
  options: 
  {
     range: 200,
     fireScroll: true,
     mode: 'vertical'
  },

  /* initialize */
  initialize: function(options, images, htmlBlocks) {
	
    this.setOptions(options);
    this.container = $(window);
    var axis = (this.options.mode == 'vertical' ? 'y': 'x');
    this.containerDimension = this.container.getSize().size[axis];
    this.start = 0;
    var curFold = this.containerDimension + this.options.range;

    // Process lazy load images
    var lazyCnt = 0;
    this.images = images.filter(function(lazyEl) {
        var el = $(lazyEl.id);
        if(el.getPosition()[axis] > curFold && lazyEl.scroll) {
          lazyCnt++;
          el.oSRC = lazyEl.src;
          return true;
        } else { // load now
          window.setTimeout( function(){
              try {
                el.src = lazyEl.src;
              } catch (e) { 
                ta.util.error.record(e, 'lazy', null, {'src': lazyEl.src }); 
              }
            }, 1500 );
          return false;
        }
      },this);
    
    // Process lazy load HTML blocks
    var lazyHtmlCnt = 0;
    this.htmlBlocks = htmlBlocks.filter(function(lazyHtml) {
        var el = $(lazyHtml.id);
        if(el && (el.getPosition()[axis] > curFold && lazyHtml.scroll)) {
          lazyHtmlCnt++;
          return true;
        } else { // load now
          window.setTimeout( function(){
              try {
                el.innerHTML = lazyHtml.data;
              } catch (e) { 
                ta.util.error.record(e, 'lazy', null, {'id': lazyHtml.id }); 
              }
            }, 1500 );
          return false;
        }

      },this);

    /* create the action function */
    var action = function() {
      var cpos = this.getScroll();
      if(cpos > this.start) {
        this.images = this.images.filter(function(lazyEl) {
            try
            {
              var el = $(lazyEl.id);
              if(el && ((this.getScroll() + this.options.range + this.containerDimension) >= el.getPosition()[axis])) {
                  if (el.oSRC != ''){
                    el.src = el.oSRC;
                    el.oSRC = '';
                  }
                  this.fireEvent('load',[el]);
                  return false;
                }
            } catch (e) { 
              ta.util.error.record(e, 'lazy', null, {'src': lazyEl.src }); 
            }
            return true;
          },this);
        
        this.htmlBlocks = this.htmlBlocks.filter(function(lazyHtml) {
            try
            {
              var el = $(lazyHtml.id);
                if(el && ((this.getScroll() + this.options.range + this.containerDimension) >= el.getPosition()[axis])) {
                  el.innerHTML = lazyHtml.data;
                  return false;
                }
            } catch (e) { 
              ta.util.error.record(e, 'lazy', null, {'id': lazyHtml.id }); 
            }
            return true;
          },this);

        // Reset furthest scroll position.
        this.start = cpos;
      }

      this.fireEvent('scroll');
      /* remove this event IF no images */
      if(!this.images.length) {
        this.container.removeEvent('scroll',action);
        this.fireEvent('complete');
      }
    }.bind(this);
	
    var reset = function()  {
      // Reset if the window was resized
      var newContainerDimension = this.container.getSize().size[axis];
      if (newContainerDimension > this.containerDimension) {
        this.containerDimension = newContainerDimension; 
        this.start = -1;
        action();
      }
    }.bind(this);	


    /* listen for scroll */
    this.container.addEvent('scroll',action);
    this.container.addEvent('resize',reset);
    if(this.options.fireScroll) { this.container.fireEvent('scroll'); }
  },

  getScroll: function() {
    return (this.options.mode == 'vertical' ? this.container.getScrollTop() : this.container.getScrollLeft());
  }

  });
LazyLoad.implement(new Events, new Options);
/** @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');
};

ta.util.Toggle.toggleTarget = function(evnt, elmt, target) {
  evnt.preventDefault();
  var open = true;
  if ($(elmt).hasClass('closed')) open = false;
  $(elmt).toggleClass('closed');
  $(target).setStyle('display', open ? 'none' : 'block');
};

/**
 * 
 * @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 ta.util.URL
 * 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);
          if (str.charAt(0) != '/') str = '/' + str;
          str = window.location.protocol + '//' + window.location.hostname + port + str;
          break;
        case 'ABSPAGE':
          var port = (window.location.port == "80" || window.location.port == "")? "" : (":" + window.location.port);
          if (str.charAt(0) != '/') str = '/' + str;
          str = window.location.protocol + '//' + window.location.hostname + port + str;
          if (str.indexOf('.html') == -1) str += '.html';
          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);
          if (str.charAt(0) != '/') str = '/' + str;
          str = window.location.protocol + '//' + window.location.hostname + port + path + str;
          break;
        default: 
          str = RegExp.$1 + ":" + str;
          break;
      }
    }
    return str;
  }
};
/** @namespace */
ta.util.element = {};

/*
 * Replace the contents of an element via XHR
 */
ta.util.element.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' } );
        (new Asset.image(CDNHOST + "/img2/generic/site/loop.gif")).injectInside(imgCntr);
        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
                if (window.behavior)
                {
                    window.behavior.apply(myElement);
                }

                //  notify anyone listening we are done
                myElement.fireEvent('onContentReplaced', [myEvent, myElement]);
            },
            evalScripts:true
        }).request();
    }
};

/**
 * Show a spinner in place of content.
 *
 * @param event the event
 * @param element the element
 */
ta.util.element.spinner = function(event, element)
{
  $(element).getParent('.spinnerReplaceable').getChildren().each(function(child){
    if (child.hasClass('spinner')) child.show();
    else child.hide();
  });
};

ta.util.element.clearDefault = function(event, element)
{
  if (element.value == element.defaultValue) element.value = "";
}
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');
    }
  },
  
  defaultOnBlur: function(event, elmt) {
    if (elmt.value == "")
    {
      elmt.addClass('focusClear');
      elmt.value =  elmt.defaultValue;
    }
  },

  validate: function(event, elmt) {
    if (elmt.q.value == "" || $(elmt.q).hasClass('focusClear'))
    {
      return false;
    }
    setOneTimeCookie("SRCHACT","NEW");
    return true;
  }

};
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: false,
        activate: ta.overlays.ACTIVATE_CLICK,
        content: document.getElement('.'+typeClass+'Info').innerHTML
      }, elmt);
    }
  },
  
  VRInquiryPrivacy:function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: false,
      activate: ta.overlays.ACTIVATE_HOVER,
      content: $('vrPrivacyBox').innerHTML
    }, elmt)
  },
 
  BLSpecialOffersUrl:function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: false,
      activate: ta.overlays.ACTIVATE_HOVER,
      content: $('blLinkMsg').innerHTML
    }, elmt)
  },

  /**
   * Create an overlay for a rate label that is too long
   */
   rateLabelOverlay:function(evnt, labelNum)
   {
     var e = new Event(evnt || window.event)
     e.stop();
     
     var elmt = $(e.target)
     elmt.onclick = null;
     new ta.overlays.SkinnyOverlay({
       showCloseButton: false,
       activate: ta.overlays.ACTIVATE_HOVER,
       content: document.getElement('.hiddenFullRateLabel' + labelNum).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;
  },
  
  /**
   * Create the full description overlay.
   */
  vrFullDescriptionFlyout:function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayAbove({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      content: $('vrFullDescription').innerHTML
    }, elmt)
  },
  
  vrFullBlurbFlyout:function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      content: $('VR_BLURB').innerHTML
    }, elmt)
  },
  
  vrMapFiltersFlyout:function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    var overlay = new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK,
      content: $('VR_MAP_FILTERS_BOX').innerHTML
    }, elmt);
    
    overlay.container.addClass('typeVR');
  },
  
  vrAvailabililtyCalander:function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    
    new Ajax('/VRDetailAjax', {
      data: { kind:'cal', vrid:elmt.id, inquiry:true, pid:3284 },
      onComplete: function(txt, xml) {
        new ta.overlays.RelativeOverlayBelow({
          showCloseButton: true,
          activate: ta.overlays.ACTIVATE_CLICK,
          content: txt
        }, elmt)
      } 
    }).request();    
  },
  
  vrRemoveRecentlyViewed:function(evnt, elmt)
  {
    evnt.stop();
    //elmt.onclick = null;
    
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: false,
      activate: ta.overlays.ACTIVATE_HOVER,
      content: $('vrRemoveRecentlyViewed').innerHTML
    }, 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 params = elmt.id.split("-");
	var uid = "";
    var src = "";
    var catid = "";
    var fus = false;
    for (var i = 0; i < params.length; i++) {
      var param = params[i];
	  if (param.match(/\bUID_([0-9A-F]+)\b/)) {
        uid = RegExp.$1;
	  } else if (param.match(/\bSRC_(\d+)\b/)) {
	    src = RegExp.$1;
      } else if (param.match(/\bCATID_(\d+)\b/)) {
        catid = RegExp.$1;
      } else if (param.match("FUS")) {
        fus = true;
      }
    }
	
    var lsoId = '';
	if (typeof(footprintGet) != 'undefined') {
	  lsoId = footprintGet();
	}
	
    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+'&src='+src+'&fus='+fus+'&LsoId='+lsoId
    }, 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;
  },
  
  /**
   * Create Best Value overlay.
   */
  travRecDeal: function(evnt, elmt) {
    evnt.stop();
    elmt.onmouseover = null;
    new ta.overlays.RelativeOverlayBelow({
      activate: ta.overlays.ACTIVATE_CLICK,
      remoteContent: '/vpages/travRecDealFly.html?log=false'
    }, elmt);
    return false;
  },
  
  starAttribution: function(evnt, elmt) {
    evnt.stop();
    elmt.onclick = null;
    new ta.overlays.RelativeOverlayBelow({
      showCloseButton: true,
      activate: ta.overlays.ACTIVATE_CLICK
    }, elmt);
    return false;
  }
}
ta.common.sendmessage = {

  // make a datestamp look purty.
  getMessageCode: function() {
    var ts = new Date().getTime();
    var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
    var bits = []; for (var i = 0; i < 32; i++) { bits[i] = ((ts & (1 << i)) != 0) ? 1 : 0 };
    var buckets = [[],[],[],[],[],[]]; for (var i = 0; i < 30; i++) { buckets[i % (buckets.length)].push(bits[bits.length-1 -i]); }
    var code = "";
    for (var i = 0; i < buckets.length; i++) code += t.substr(parseInt(buckets[i].reverse().join(""), 2),1);
    return code;
  },


  /**
   * Shows the Send Message popup
   * 
   * @param evnt click event
   * @param elmt the anchor element
   */
  showSendMessage: function(evnt, elmt) 
  {
    evnt.stop();

    //prevent multiple fast clicks from opening more than 1 lightbox
    if(ta.has('overlays.sendmessage.loading'))
    {
      return;
    } else {
      ta.store('overlays.sendmessage.loading', true);
      ta.remove.delay(500,null,'overlays.sendmessage.loading');
    }
    
    var smo = ta.retrieve('overlays.sendmessage');
    var uri = elmt.href;

    if (smo && uri.match(/notyou=1/)) {
      smo.hide(evnt);
    } else if (smo) {
	  return;
	}
    
    var showRecaptcha = elmt.className.match(/\b(callback_ShowRecaptcha)\b/);
    var sCBFuncName;

    if (showRecaptcha) {
      new Asset.javascript('http://api.recaptcha.net/js/recaptcha_ajax.js');
      sCBFuncName = "callback_ShowRecaptcha";
    }
    
    if (!userLoggedIn || migrationMember) {
      if (!(ops = elmt.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];
      ta.store("community.needsReload", true);
      window.loginSuccess = function() {
        if(!reg_overhaul) hideIFrame();
        ta.common.sendmessage.communityLightbox(uri, sCBFuncName);
      }
      if(reg_overhaul) {
        var regFlow = msgType == "cm" ? "PRIVATE_MESSAGE" : "COMPLIMENT_USER";
        ta.overlays.registration.connect({flow: regFlow});
      }
      else {
        var func = !userLoggedIn ? login : migrate;
        func(['tt',msgType,'greeting',greeting,'returnTo', getRelativeURL()]);
      }
      return;
    }

    ta.common.sendmessage.communityLightbox(uri, sCBFuncName);
  },
  
  /**
   * Shows a lightbox and calls a callback function.
   * If community is not enabled, it prompts the user to reenable community first
   *
   * @param uri the href of the AJAX request to get the lightbox contents
   * @param sCBFuncName the callback function name if one exists
   */
  communityLightbox: function(uri, sCBFuncName)
  {
    if (!communityEnabled) {
      window['callback_communityLightboxEnable'] = function() {
        communityEnabled = true;
        var lb = ta.retrieve('overlays.sendmessage');
        if (lb) lb.hide();
        ta.common.sendmessage.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="ta.common.sendmessage.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
      var smlb = ta.overlays.showInLightbox(aSMC.toString(), { backdrop: ta.overlays.BACKDROP_NEVER, onHide: ta.common.sendmessage.cancel.bind(this) } );
      smlb.container.setStyle('z-index', '9998');
      ta.store('overlays.sendmessage', smlb);
      return;
    }
    
    if (sCBFuncName) {
      var callback_communityLightbox = function(r) {
        var smlb = ta.overlays.showInLightbox(r, { backdrop: ta.overlays.BACKDROP_NEVER, permanent:true, onHide: ta.common.sendmessage.cancel.bind(this) } );
        smlb.container.setStyle('z-index', '9998');
        ta.store('overlays.sendmessage', smlb);      
        var cbfn = window[sCBFuncName];
        if ($defined(cbfn) && $type(cbfn) == "function") {
          cbfn();
        }
      };
    }
    else {
      var callback_communityLightbox = function(r) {
        var smlb = ta.overlays.showInLightbox(r, { backdrop: ta.overlays.BACKDROP_NEVER, permanent:true, onHide: ta.common.sendmessage.cancel.bind(this) } );
        smlb.container.setStyle('z-index', '9998');
        ta.store('overlays.sendmessage', smlb);        
      };
    };
    
    var ops = {
      onComplete: callback_communityLightbox,
      evalScripts: true,
      onFailure: function(e) {
        alert(JS_Ajax_failed);
      }
    };
    
    new Ajax(uri, ops).request();
  },

  /**
   * Reenables the community functionality for a specific user
   *
   * @param callback
   * @param noHandle
   */   
  enableCommunity: function(callback, noHandle) {
    var res = callback;
    if (!noHandle) res = function(r) { ta.common.sendmessage.enableCommunityResponse(r, callback); }
    new Ajax("/MemberSubscriptionsController?Action=ChangeM2M&set=on", {
      onComplete: res,
      onFailure: function(e) { alert(JS_Ajax_failed); }
    }).request();
  },

  /**
   * The AJAX response handler when reenabling community
   *
   * @param res
   * @param callback
   */
  enableCommunityResponse: function(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';
    }
  },
  
  /**
   * Attempts to send a message
   *
   * @param evnt click event
   * @param elmt the anchor element
   */
  submitSendMessage: function(evnt, elmt)
  {
    evnt.stop();
    elmt.onclick = null;

    var lsoId = elmt.className.match(/LSO_(\w+)/)[1];
    $('LsoId').value = footprintGetWithSet(lsoId);
    $('screenWidth').value = screen.width;
    $('screenHeight').value = screen.height;
  
    var uri = elmt.href;
    if (!elmt.className.match(/\bform_(\w+)\b/)) return;
    var form = $(RegExp.$1);
    var ops = {
      data: form,
      evalScripts: true,
      onFailure: function(e) {
        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) {
      var smlb = ta.overlays.showInLightbox(text, { backdrop: ta.overlays.BACKDROP_NEVER, permanent:true, onHide: ta.common.sendmessage.cancel.bind(this)} );
      ta.store('overlays.sendmessage', smlb);        
      if ($defined(cbfn) && $type(cbfn) == "function") {
        cbfn();
      }
    }

    var fn = window['validate'+form.id]; 
    if ($defined(fn) && $type(fn) == "function" && fn.apply(form) == false)
      return;

    if (elmt.className.match(/\bevalScripts\b/)) ops.evalScripts = true;  
    var smo = ta.retrieve('overlays.sendmessage');
    if (smo && smo.options && smo.options.permanent) {
      smo.options.permanent = false;
    }
    new Ajax(uri, ops).request();
  },
  
  /**
   * cleans up PM state
   *
   * @param programmatic false if user closed overlay
   */
  cancel: function(programmatic)
  {
    if (programmatic) return; // only care if user closed overlay

    var smo = ta.retrieve('overlays.sendmessage');
	if (smo) {
      ta.remove('overlays.sendmessage');
	}
	
	if (ta.has("community.needsReload")) {
      window.location.reload();
    }
  },

  /**
   * Attempts to close the "send message" overlay, reloading the page if necessary
   *
   * @param evnt click event
   * @param elmt the anchor element
   */
  closeSendMessage: function(evnt, elmt)
  {
    if (evnt && evnt.stop) evnt.stop();
    if (elmt) elmt.onclick = null;

    var smo = ta.retrieve('overlays.sendmessage');
    if (smo) {
        smo.hide(evnt);
    }
  }
  
};
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);
  },

  reportGeoCode: function(event, elmt) {
    event.preventDefault();
    var locId = elmt.id.match(/loc_([0-9]+)/);
    if (locId && locId.length > 1)
    {
      locId = locId[1];
      new Ajax('/ReportIAP?action=geocode&locationid=' + locId, {
        update: elmt.getParent()
            }).request();
    }
  }

}
ta.servlet.Member = {

  memberBenefitsSubscribe: function(event) {
    ta.servlet.Member.processMOSubscribeRequest(event);
    return false;
  },
  
  memberGuideRequest: function(event) {
    processGuideRequest(event);
  },

  memberTOGForm: function(event) {
    processTOGForm(event);
  },
  
  processMOSubscribeRequest: function(elmt) {
    if (!loggedIn) {
        new Event(elmt).preventDefault();
        return false;
    }

    var uri = "/TravelGuides"
    var form = $("MEMONLY_SUBS_FORM");
    form.elements.action.value='subscribe';
    var ops = {
        data: form,
        onFailure: function(e) { alert(e.status + "\n" + e.responseText); }
    }
    ops.onComplete = ta.servlet.Member.subscribeRequestResFunc;
    var fn = window['validate'+form.id]; 
    new Event(elmt).preventDefault();
    new Ajax(uri, ops).request();
  },
  
  subscribeRequestResFunc: function(respTxt) {
    if (respTxt != '') {
        for (subtype in subtypes) {          
            if (subtyperes[subtype].test(respTxt)) {
            	if(subtype=='tw' && ta.retrieve('tcd.refresh')) {
            	  window.location.reload();
            	}
                var subCB=document.getElementById(subtype + 'Optin');
                subCB.setAttribute('checked','true');
                subCB.setAttribute('disabled','true');
                var subLbl = document.getElementById(subtype + 'OptinLbl');
                subLbl.innerHTML = "<b>" + headings[subtype] + "</b> " + tamg_thanks;
                var errorDiv = document.getElementById('OPTINTEXT');
                errorDiv.style.display = 'none';
            }
        }

        //need to remove subscribe button?
        var checked = {};
        for (subtype in subtypes) {
          var cb = document.getElementById(formids[subtype]);
          checked[subtype] = cb.checked;
        }
        
        var bNoResult = true;
        var bAllChecked = true;
        for(subtype in checked) {
            if (subtyperes[subtype].test(respTxt)) {
                bNoResult = false;
            } 
            if (!checked[subtype]) {
                bAllChecked = false;
            }
        }

        if (bAllChecked) {
            var nlButton = document.getElementById('NLSUBMITDIV');
            nlButton.style.display = 'none';
        }

        if (bNoResult) {
            var node=document.getElementById('OPTINTEXT');
            node.innerHTML = "<div class=\"alert\">" + no_selection_error + "</div>"
            node.style.display = 'block';
        }
    }
  },
  
  addTripWatchLocation: function(event,elmt,updateTxt)
  {
    var mElmt = new Element(elmt);
    new Event(event).stop();
    new Ajax(elmt.href, {
      onFailure: function(e) { alert(JS_Ajax_failed); },
      onComplete: function(txt,xml) {
        if(txt.match(/^{/)) {
          var data = eval( '(' + txt + ')' );
          if(data.ERROR) {
            alert(data.ERROR);
            return;
          }
        }
        var spanElt = new Element('span');
        spanElt.setHTML(updateTxt);
        mElmt.hide();
        spanElt.injectAfter(mElmt);
      }
    }).request(); 
  },
  
  addTripWatchLocationReload: function(elmt)
  {
    new Ajax(elmt.href, {
      onFailure: function(e) { alert(JS_Ajax_failed); },
      onComplete: function(txt,xml) {
        window.location.reload();
      }
    }).request();
  }
};
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;
    }
  },

  /**
   * Generic popup. Size based on classname: sz(w)X(h)  
   */
  openSizedPopup: function(evnt, elmt)
  {
    new Event(evnt).preventDefault();
    var width = 600;    
    var height = 400;
    if (sz = elmt.className.match(/sz(\d+)x(\d+)/)) {
      width = sz[1];
      height = sz[2];
    }
    ta.servlet.Popup.open(elmt.href, 'photoTPW', width, height, false, false, true, true);
  },
  
  /**
   * 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, leaveOpen) {
    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;
      if (! (typeof leaveOpen != 'undefined' && leaveOpen) ) {
        w.close();
      } else {
        window.blur();
      }
    }
  },

  /**
   * 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 if (elmt.getParent().getTag() == 'a'){
        url = elmt.getParent().href;
      } else {
        return;
      }

      // Get the window name from the class name winXXX if it is present
      var winName = elmt.className.match(/win(.+)/);
      if (winName && winName.length>1) {
        winName = winName[1];
      } else {
        winName = null;
      }

      ta.servlet.Popup.open(url,winName,980,800,240,5,false,true);
  },

  openProviderWindow: function(evnt, elmt)
  {
    new Event(evnt).preventDefault();
    ta.servlet.Popup.open(elmt.href, 'photoTPW', 1260, 922, false, false, true, true);
  }
  
};
ta.servlet.Hotel = {

  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;
  },
  
  //Hotel Summarization overlay
  showReviewSummary: function(event, elmt)
  {
      new Event(event).preventDefault();
      //url for hotel summarization data
      var conceptId = elmt.id;
      var conceptWords = conceptId.split('_');
      var concept = conceptWords[1];
      concept = concept.split('-');
      concept = concept.join(' ');
      var hsUrl = 'http://' + document.domain + '/HotelSummary?d=' + modelLocId + '&concept='+ concept;
      var hideOverlay = false;
      // display results of below ajax call
      // in an overlay
      var compFunc = function(res) {
        var pwrOverlay = new ta.overlays.CenteredOverlay({
          backdrop: ta.overlays.BACKDROP_NEVER,
          style: 's3 dg nopadding',
          showCloseButton: true
        }, elmt).loadRemoteSuccess(res);
        
        if(pwrOverlay.inner)
        {
          var emptySnippets = $('emptySnippets');
          if(emptySnippets)
          {
            hideOverlay = true;
          }
        }
        else
        {
          hideOverlay = true;
        }
        if(hideOverlay)
        {
          pwrOverlay.hide();
        }
      };
      
      //make the ajax request for the also viewed content
      new Ajax(hsUrl, {
        onComplete: compFunc,
        evalScripts: true 
      }).request();
  }


};
/**
 * 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,
    monthHeadingFormat: ""
  },

  /**
   * @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(''));
    
    if(this.options.monthHeadingFormat=="" && DATE_FORMAT_MMM_YYYY) {
      this.options.monthHeadingFormat = DATE_FORMAT_MMM_YYYY;
    }
  },
  
  /**
   * 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(this.options.monthHeadingFormat.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);
/**
 *  @class
 */
ta.widgets.tabContainer = 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    
    return 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');});
                    ta.util.element.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');
        }
    });
};/** 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 ta.overlays.Overlay
 *
 * @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_*
 * @option {boolean} [permanent=false] Whether or not to keep open until user hits the close button
 */
ta.overlays.Overlay = new Class({
  options: {
    requestData:      null,
    showCloseButton:  false,
    style:            'typeO',
    backdrop:         ta.overlays.BACKDROP_NEVER,
    permanent:          false,
    isChild:          false,
    remoteContent:    null,
    keepElementInDocument: false,
    requestMethod: 'get'
  },
  
  /**
   * @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'
    }).injectTop(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',
          allowtransparency: 'true',
          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,
        filter: 'alpha(opacity=0)',
        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.
    // permanent option now available for all overlays
    $$('.overlay').each(function(other){
      if (other.overlay == this) return;
      if (this.parentOverlay && other.overlay == this.parentOverlay) return;
      if (other.overlay.options.permanent) return;
      other.overlay.hide(evnt);
    }, this);
    
    $(document.body).adopt(this.container);
    this.position();
    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.options.keepElementInDocument && this.container.style.left == '-999em') ||
      (!this.options.keepElementInDocument && !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'
    });
    if (!this.options.keepElementInDocument) this.container.remove();
    this.visible = false;
    this.fireEvent('onHide',!evnt);
    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 + '/img2/generic/site/loop.gif', {
          onload: this.position.bind(this),
          'class': 'anim_loop'
        }).injectInside(this.inner);
    }
    new Ajax(ta.util.URL.parse(uri), {
      method: this.options.requestMethod,
      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,
    attachCloseEvent: 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.options.attachCloseEvent) {
      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;
/** Show the flyout when mousing over the source element for a length of time (fixed at 300ms for now) */
ta.overlays.ACTIVATE_HOVER_DELAY  = 4;
/** Show the flyout on both hover and mouse click */
ta.overlays.ACTIVATE_BOTH = 5;

/** 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);

    this.showHandlerWithDelay = this.showWithDelay.bindWithEvent(this);
    this.hideHandlerWithDelay = this.hideWithDelay.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_HOVER_DELAY:
        this.source.addEvent('mouseenter', this.showHandlerWithDelay);
        this.source.addEvent('mouseleave', this.hideHandlerWithDelay);
        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);
    }
    
    // ID contents
    var contents = $(this.source.id + '_CONTENTS');
    if (contents) {
      this.inner.setHTML(contents.innerHTML);
      contents.remove();
      this.runBehaviorOnFirstShow = true;
    }

    if (this.options.autoShow) this.show();
    else if(this.options.delayShow) this.showWithDelay();
  },
  
  /**
   * 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);

    $clear(this.showTimeout);
    
    if ((this.options.activate == ta.overlays.ACTIVATE_HOVER || this.options.activate == ta.overlays.ACTIVATE_HOVER_DELAY) && 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:
      case ta.overlays.ACTIVATE_HOVER_DELAY:
        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(eTarg)) return;
    }
    switch (this.options.activate) {
      case ta.overlays.ACTIVATE_HOVER:
      case ta.overlays.ACTIVATE_HOVER_DELAY:
        //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 {
          $clear(this.hideTimer);
          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() {
    switch (this.options.activate) {
      case ta.overlays.ACTIVATE_HOVER:
    this.source.removeEvent('mouseenter', this.showHandler);
    this.source.removeEvent('mouseleave', this.hideHandler);
        break;

    case ta.overlays.ACTIVATE_HOVER_DELAY:
    this.source.removeEvent('mouseenter', this.showHandlerWithDelay);
    this.source.removeEvent('mouseleave', this.hideHandlerWithDelay);
        break;
}
      
    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() {
switch (this.options.activate) {
      case ta.overlays.ACTIVATE_HOVER:
    this.source.addEvent('mouseenter', this.showHandler);
    this.source.addEvent('mouseleave', this.hideHandler);
        break;
 case ta.overlays.ACTIVATE_HOVER_DELAY:
    this.source.addEvent('mouseenter', this.showHandlerWithDelay);
    this.source.addEvent('mouseleave', this.hideHandlerWithDelay);
        break;
}
    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;
  },

  showWithDelay: function(evnt) {
    evnt = evnt || window.event;
    $clear(this.showTimeout);
    if(this.visible) {
        return this.show(evnt);
    } else {
        this.showTimeout = this.show.delay(300, this);
    }
  },

  hideWithDelay: function(evnt) {
    evnt = evnt || window.event;
      if(!this.visible) {
        $clear(this.showTimeout);
      }
      else {
        return this.hide(evnt);
      }
  }
});
/**
 * 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();
    var tamgFooter = document.getElementById('tamgFooter');
    if (tamgFooter){
      if (tamgFooter.offsetHeight) { bottomEdge = bottomEdge - tamgFooter.offsetHeight; }
      else if (tamgFooter.clientHeight) { bottomEdge = bottomEdge - tamgFooter.clientHeight; }
    }
    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;
    if (this.options.scrollContainer) {
      posTop -= this.options.scrollContainer.scrollTop;
    }
    // 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.options.scrollContainer) {
      posTop -= this.options.scrollContainer.scrollTop;
    }
    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 the style for commerceOverlay
   */
  _getCommerceOverlayStyle: function() {
    var DHTML_CR_REDESIGN = (false&&window.cookieDomain!='.daodao.com');
    return 'commerceOverlay'+(DHTML_CR_REDESIGN?" dhtmlcrRedesign":"");
  },
  
  /**
   * 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: ta.overlays.Factory._getCommerceOverlayStyle(),
      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: ta.overlays.Factory._getCommerceOverlayStyle(),
      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: ta.overlays.Factory._getCommerceOverlayStyle(),
      showCloseButton: true,
      onLoad: ta.commerce.checkrates.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: ta.overlays.Factory._getCommerceOverlayStyle(),
      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: ta.overlays.Factory._getCommerceOverlayStyle(),
      showCloseButton: true,
      onLoad: ta.commerce.checkrates.commCopyUpdate
    }, elmt);
  },
  
  relBelowS4Dg: function(evnt, elmt) {
	    evnt.stop();
	    elmt.onclick = null;
	    new ta.overlays.RelativeOverlayBelow({
	      showCloseButton: true,
	      style: 's4 dg',
	      activate: ta.overlays.ACTIVATE_CLICK
	}, 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 above the element. Should be called onmouseover.
   *
   * @param {Event} evnt The Event
   * @param {Element} elmt The element
   */
  relAboveTAMGFooter: function(evnt, elmt) {
    elmt.onmouseover = null;
    var hasPageDiv = $('PAGE');  // needed for positioning
    if(hasPageDiv) {
      new ta.overlays.RelativeOverlayAbove({
        activate: ta.overlays.ACTIVATE_HOVER,
        style: 's3 vdg ovrFooter'
      }, 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 a lightbox from the hidden contents of the spawning element.
   *
   * @param {Event} evnt the event
   * @param {Element} elmt source element
   */
  localLightbox: function(evnt, elmt)
  {
    elmt.onclick = null;
    var contents = elmt.getElement(".overlayContents").remove();
    var lb = new ta.overlays.CenteredOverlay({
      showCloseButton: true,
      backdrop: ta.overlays.BACKDROP_ALWAYS
    }).loadRemoteSuccess(contents.innerHTML);
    elmt.addEvent('click', lb.showHandler);
  },
  
  /**
   * 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);
  },

  /**
   * Creates an overlay in the center of the screen, with a backdrop, showing the full size version
   * of the image.
   * @param {Element} elmt img tag of the thumbnail image
   */
  zoom: function(elmt)
  {
    elmt = $(elmt);
    if (!elmt.src) return;
    var src = elmt.src.replace(/t\./, '.');
    var overlay = new ta.overlays.CenteredOverlay({
      showCloseButton: false,
      style: 's4 lg np',
      backdrop: ta.overlays.BACKDROP_ALWAYS
    });
    elmt.onclick = '';
    elmt.addEvent('click', overlay.show.bind(overlay));
    var img = new Asset.image(src, {onload: function() {overlay.position();}, style: 'cursor:pointer'});
    img.addEvent('click', overlay.hide.bind(overlay));
    if (elmt.alt && elmt.alt != '') new Element('div', {styles: {fontWeight: 'bold'}}).setText(elmt.alt).inject(overlay.inner);
    overlay.inner.adopt(img);
    overlay.position();
  },

  relRightRemoteHLB_optionalDelay: function(evt, elmt) {
  	elmt = $(elmt);
  	if(elmt.getTag() != 'div')
  	{
  		elmt = elmt.getParent();
  	}
  	var uri = _findRemoteURI(elmt);
  	if (uri == null) return;
    var noDelayFirst = elmt.className.match(/noDelayFirst/);
    var delayOpen = elmt.className.match(/delayOpen/);
    var delayedFirst = ta.retrieve('delayedFirst');
    var autoShow = !delayOpen || (noDelayFirst && !delayedFirst);
    var overlay = new ta.overlays.RelativeOverlayRight({
      activate: (delayOpen ? ta.overlays.ACTIVATE_HOVER_DELAY : ta.overlays.ACTIVATE_HOVER),
      pinnable: ta.overlays.PINNABLE_CLICK,
      backdrop: ta.overlays.BACKDROP_PINNED,
      style: ta.overlays.Factory._getCommerceOverlayStyle(),
      showCloseButton: true,
      autoShow: autoShow,
      delayShow: !autoShow
    }, elmt);
    ta.store('delayedFirst', true);
  }
}
})();
/**
 * 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){
    if(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
 * @option {boolean} [keepDelta=false] attempt to maintain date span when adjusting start or end date
 */
ta.overlays.PairedCalendar = new Class({
  options: {
    validate: false,
    allowSameDay: false,
    calendarOptions: null,
    defaultSpan: 1,
    keepDelta: false
  },

  /**
   * @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';
    }
    var clearDates = $('clearDates');
    if(clearDates) clearDates.style.visibility = 'visible'; 
    
    // 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);
      if (this.options.keepDelta) {
        this.options.defaultSpan = 1;
      }
      var d = new Date(this.after.calendar.options.firstDate);
      d.setDate(d.getDate() + this.options.defaultSpan);
      this.after.calendar.selectedDate = d;
      this.before.updateFields();
      this.after.updateFields();
    }
    else {
      if (overlay == this.before){
        if (this.options.keepDelta || !this.isBeforeValid()) {
          var d = new Date(this.before.calendar.selectedDate);
          d.setDate(d.getDate() + this.options.defaultSpan);
          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() - this.options.defaultSpan);
          if (d.getTime() < this.before.calendar.options.firstDate.getTime()) {
            d = new Date(this.before.calendar.options.firstDate.getTime());
          }
          this.before.calendar.selectedDate = d;
        }
        this.before.updateFields();
      }
    }
    if (this.options.keepDelta && this.before.calendar.selectedDate) {
      this.options.defaultSpan = ta.util.date.getDaysInRange(this.before.calendar.selectedDate, this.after.calendar.selectedDate) - 1;
      if (this.sameDayEnabled()) {
        if (this.options.defaultSpan < 0) {
          this.options.defaultSpan = 0;
        }
      } else {
        if (this.options.defaultSpan < 1) {
          this.options.defaultSpan = 1;
        }
      }
    }
    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.createSpannedPairedCalendar = function(evnt, elmt, span){
  new Event(evnt).stop();
  new ta.overlays.PairedCalendar(elmt, {defaultSpan: span});
}

/**
 * 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();
  return 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();
  }
}

ta.overlays.toggleOnSearchAll = function(elmt){
  elmt = $(elmt);
  var form = elmt.getParent('form');
  if (!form.calendar) new ta.overlays.PairedCalendar(elmt, {validate:true});
  if(form.calendar && form.searchAll){
    if (form.searchAll.type == 'checkbox') form.searchAll.checked = true;
    else if (form.searchAll.type == 'hidden') form.searchAll.value = 'true';
    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, options){
  elmt = $(elmt);
  elmt.onclick = "";
  var form = elmt.getParent('form');
  if(!options) {
    options = {validate: true};
  }
  if (!form.calendar) new ta.overlays.PairedCalendar(elmt, options);
  form.calendar.toggleSearchAll(elmt);
  elmt.addEvent('click', form.calendar.toggleSearchAll.bind(form.calendar));
}

// remove dates and the 'Clear dates' link, and perform and undated search, 
// maintaining the filters that are currently set, except the min stay filter
ta.overlays.clearDates = function(evnt, clearDatesId){
    ta.overlays.toggleOnSearchAll(clearDatesId);
    $(clearDatesId).style.visibility='hidden';
    
    // if we're on a page which includes lhn filters (e.g. overview page or VRAC)
    // we need to clear the min stay filter before rerunning the search  
    var minStay = $('minStay');
    if(minStay !== null) {
        $('minStay').selectedIndex = 0; // no dates means no min stay
        filtersChanged(evnt);
    }
}
/**
 * A permanent overlay positioned absolutely in the window.
 */
ta.overlays.PermanentOverlay = ta.overlays.AbsoluteOverlay.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent($merge({permanent:true}, options), elmt);
  }
});
/**
 * Convenience class for creating an overlay below that is at most 120px wide.
 * @class
 */
ta.overlays.SkinnyOverlay = ta.overlays.RelativeOverlayBelow.extend({
  /**
   * @param {Object} options Options, see below.
   * @param {Element} elmt Element the triggers the flyout.
   */
  initialize: function(options, elmt) {
    this.parent(options, elmt);
    
    this.container.setStyle('max-width', '120px');
    
    if (window.ie6) {
        this.shim.setStyle('max-width', '120px');
    }
  }
});/**
 * Registration flow controller.
 *
 * @option {string} [flow=null] flow to use
 */
ta.overlays.Registration = new Class({
  options: {
    flow: null,
    url: '/Registration',
    confirmCloseMsg: null
  },

  /**
   * @param {Object} options Options, see below.
   */
  initialize: function(options)
  {
    this.setOptions(options);

    this.responseHandler = this.response.bind(this);
    this.failureHandler = this.failure.bind(this);
    
    this.closeConfirmTest = false;
    
    if(this.closeConfirmTest) {
      this.overlay = new ta.overlays.CenteredOverlay({
        backdrop: ta.overlays.BACKDROP_ALWAYS,
        autoShow: false,
        style: 's4 dg'
      });
      this.overlay.closeBtn.removeEvents('click');
      this.overlay.closeBtn.addEvent('click', this.cancel.bind(this));
    }
    else {
      this.overlay = new ta.overlays.CenteredOverlay({
        backdrop: ta.overlays.BACKDROP_ALWAYS,
        autoShow: false,
        style: 's4 dg',
        attachCloseEvent: false
      });
      
      this.overlay.closeBtn.addEvent('click', this.cancel.bind(this));
    }
  },

  getRequestData: function()
  {
    var data = {
      flow: this.options.flow
    };
    if (ta.has('registration.data')) data = $merge(data, ta.retrieve('registration.data'));
    if (ta.has('page.geo')) data.geo = ta.retrieve('page.geo');
    data.source = pageServlet;
    data = [data];
    $A(arguments).each(function(arg){
      data.push(arg);
    });
    return data;
  },

  /**
   * Send a GET request to the registration handler.
   *
   * @param action action to request
   * @param parameters additional parameters to send
   */
  request: function(action, parameters)
  {
    new Ajax(this.options.url, {
      method: 'get',
      evalScripts: true,
      data: this.getRequestData(parameters, {action: action}),
      onComplete: this.responseHandler,
      onFailure: this.failureHandler
    }).request();
  },

  /**
   * Send a POST request to the registration handler.
   *
   * @param parameters form or data to submit
   */
  post: function(parameters)
  {
    new Ajax(this.options.url, {
      method: 'post',
      evalScripts: true,
      data: this.getRequestData(parameters),
      onComplete: this.responseHandler,
      onFailure: this.failureHandler
    }).request();
  },

  /**
   * Sends a CANCEL request to the registration handler.
   *
   * @param programmatic false if user closed overlay
   */
  cancel: function()
  {
    //if (programmatic && !this.closeConfirmTest) return; // only care if user closed overlay
    
    var doHide = true;
    if(this.closeConfirmTest) {
      var currentAction = ta.retrieve('registration.action');
      if(currentAction == 'SIGNIN' || currentAction == 'SIGNUP') {
        doHide = confirm(this.options.confirmCloseMsg);
      }
    }
    if(doHide) {
      new Ajax(this.options.url,{
        method: 'get',
        evalScripts: true,
        data: this.getRequestData({CANCEL:'y', action:ta.retrieve('registration.action')}),
        onComplete: this.responseHandler,
        onFailure: this.failureHandler
      }).request();
    }
  },

  /**
   * Sends a GET request to LOG the given action. Ignores the response.
   *
   * @param action
   */
  log: function(action)
  {
    new Ajax(this.options.url,{
      method: 'get',
      evalScripts: false,
      data: this.getRequestData({action:'LOG', pageAction:action})
    }).request();
  },

  /**
   * Handles response from registration handler.
   *
   * @param txt response body
   */
  response: function(txt)
  {
    if(!ta.remove('registration.noop'))
    {
      this.overlay.loadRemoteSuccess(txt);
      if (this.overlay.visible) this.overlay.position();
      else this.overlay.show();

      if(ta.remove('registration.renderFBLogin') && ta.has('facebook.available') && ta.retrieve('facebook.available'))
      {
        FB.XFBML.LoginButton($('FBC_LOGIN'));
      }
    }

    if (ta.has('registration.afterUpdate'))
    {
      var fn = ta.remove('registration.afterUpdate');
      setTimeout(fn,1);
    }
  },
  
  /**
   * Handles from request/post/cancel ajax requests.
   */
  failure: function(transport)
  {
    var status = 0;
    try { status = transport.status;} catch(e){};
    if(status==503) alert(JS_maintenance);
    else alert(JS_Ajax_failed);
  },

  /**
   * Finishes the registration flow.
   */
  close: function()
  {
    this.overlay.hide();
    this.fireEvent('onComplete');
  }

  /**
   * Fired when the registration flow is complete.
   * @name ta.overlays.Registration#onComplete
   * @event
   */
});
ta.overlays.Registration.implement(new Options, new Events);

/**
 * Public API for registration flow.
 * @class ta.overlays.registration
 */
ta.overlays.registration = {

  /**
   * Returns the current instance, or creates one if it does not exist.
   *
   * @param options options to pass when creating a new instance
   */
  getInstance: function(options)
  {
    var r = ta.retrieve('registration.instance');
    if (!r)
    {
      r = new ta.overlays.Registration(options);
      ta.store('registration.instance', r);
    }
    return r;
  },

  /**
   * Create a new ta.overlays.FBConnector and initiate the flow.
   */
  connectFB: function(event, elmt, flow)
  {
    if (event) event.preventDefault();
    if (!ta.has('registration.instance')) ta.overlays.registration.getInstance({flow:flow || 'FBC'});
    else ta.retrieve('registration.instance').overlay.hide();
    ta.util.pending.unlock('facebook.login');
    ta.util.pending.unlock('facebook.connect');
    ta.store('registration.facebook', new ta.support.FBConnector());
  },

  /**
   * Logs the user out of Facebook and re-initializes the flow.
   */
  restartFB: function(revoke)
  {
    var fbc = ta.retrieve('registration.facebook');
    if (fbc) {
      ta.util.pending.unlock('facebook.login');
      ta.util.pending.unlock('facebook.connect');
      fbc.restart(revoke, function(){
        if (ta.has('registration.instance')) ta.retrieve('registration.instance').overlay.hide();
        fbc.requestLogin();
      });
    }
    else ta.overlays.registration.connectFB();
  },

  /**
   * Create a new ta.overlays.Registration object and initiate the flow.
   * @param options the set of options to pass to ta.overlays.Registration
   */
  connect: function(options, action)
  {
    if(ta.has('facebook.available') && ta.retrieve('facebook.available')) {
      var params = {'fbAvailable':true};
    }
    else if(ta.has('registration.forceFBAvailable')) {
      var params = {'fbAvailableForce':ta.remove('registration.forceFBAvailable')};
    }
    var defaultAction = 'SIGNIN';
    ta.overlays.registration.getInstance(options).request(action || defaultAction, params);
  },

  /**
   * POSTs a form in the registration flow and renders the result.
   * @param form form or data to post
   */
  post: function(form, event)
  {
    var e = event || window.event;
    if (e) new Event(e).stop();
    ta.overlays.registration.getInstance().post(form);
  },

  /**
   * Sends a GET request for the given action and renders the result.
   * @param action action to request
   * @param parameters additional parameters for the request
   * @param options optional, options to use when creating the Registration instance
   */
  request: function(action, parameters, options)
  {
    if(action == 'SIGNUP' || action == 'SIGNIN')
    {
      parameters = $merge({'fbAvailable':ta.has('facebook.available') && ta.retrieve('facebook.available')}, parameters); 
    }
    ta.overlays.registration.getInstance(options).request(action, parameters);
  },

  /**
   * Sends a CANCEL request for the current action and renders the result.
   */
  cancel: function()
  {
    ta.overlays.registration.getInstance().cancel(false);
  },

  /**
   * Sends a GEt request to LOG the given action.
   * @param action
   */
  log: function(action)
  {
    ta.overlays.registration.getInstance().log(action);
  },

  /**
   * Finishes the registration flow.
   */
  close: function()
  {
    var r = ta.remove('registration.instance');
    if (r) r.close();
  },
  
  /**
   * Switch the flow stored in the registration instance
   */
  switchFlow: function(newFlow)
  {
    ta.overlays.registration.getInstance().options.flow = newFlow;
  }
};
/**
 * Takes an element in the page, and puts a lightbox behind it, keeping it positioned at the
 * same location. Also puts a dummy element in its place to hold the space so the page doesn't
 * jump with a reflow.
 */
ta.overlays.PopoutOverlay = ta.overlays.Overlay.extend({
  options: {
    backdrop: ta.overlays.BACKDROP_ALWAYS,
    showCloseButton: true
  },

  initialize: function(options, elmt) {
    this.parent(options, elmt);

    this.placeholder = new Element('div').injectBefore(this.source).adopt(this.source);
    this.placeholder.setStyles({
      marginBottom: this.source.style.marginBottom,
      marginTop: this.source.style.marginTop
    });
    this.source.setStyles({marginBottom: 0, marginTop:0});

    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideHandler);
    }

    this.show();
  },

  show: function(evnt) {
    // pull the source element out of the page, keeping its position
    this.sourceCoords = this.source.getCoordinates();
    this.placeholder.setStyles({
      width: this.sourceCoords.width,
      height: this.sourceCoords.height
    });
    this.inner.adopt(this.source);
    this.parent(evnt);
  },

  hide: function(evnt) {
    this.parent(evnt);

    // put the source element back in the page
    this.placeholder.adopt(this.source).setStyles({
      width: 'auto',
      height: 'auto'
    });
  },

  /**
   * Positions the container.
   */
  position: function() {
    var y = parseInt(this.container.getStyle('borderTopWidth').replace(/px/,''));
    var x = parseInt(this.container.getStyle('borderLeftWidth').replace(/px/,''));
    this.container.setStyles({
      left: this.sourceCoords.left - x,
      top: this.sourceCoords.top - y,
      width: this.sourceCoords.width,
      display: 'block'
    });
    this.positionShim();
  }
});
/**
 * Takes an element in the page, and puts a lightbox behind it, keeping it positioned at the
 * same location. Also puts a dummy element in its place to hold the space so the page doesn't
 * jump with a reflow. The overlay itself is positioned relative to this element.
 *
 * @option {string OR object} the aspect of the relativeTo element that this element should be positioned. See Mootools Element.Position. Currently IGNORED until MooTools is upgraded.
 * @option {string OR object} the edge of the element to set relative to the relative element's corner; this way you can specify to position this element's upper right corner to the bottom left corner of the relative element. This is optional; the default behavior positions the element's upper left corner to the relative element unless position == center, in which case it positions the center of the element to the center of the relative element. Currently IGNORED until MooTools is upgraded.
 */
ta.overlays.PopoutWithOverlay = ta.overlays.Overlay.extend({
  options: {
    position: 'topleft',
    edge: 'topright',
    offset: {x: -10, y:0},
    scrollOffset: -20,
    backdrop: ta.overlays.BACKDROP_ALWAYS,
    showCloseButton: true
  },

  initialize: function(options, elmt) {
    this.parent(options, elmt);

    this.placeholder = new Element('div').injectBefore(this.source).adopt(this.source);
    this.popout = new Element('div', {
      styles: {
        position: 'absolute',
        zIndex: '10002',
        fontSize: '0.75em',
        display: 'none'
      }
    }).injectInside(document.body);

    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideHandler);
    }

    this.show();
  },

  show: function(evnt) {
    this.parent(evnt);

    // pull the source element out of the page, keeping its position
    var sourceCoords = this.source.getCoordinates();
    this.placeholder.setStyles({
      width: sourceCoords.width,
      height: sourceCoords.height,
      marginBottom: this.source.style.marginBottom,
      marginTop: this.source.style.marginTop
    });
    this.popout.setStyles({
      left: sourceCoords.left,
      top: sourceCoords.top,
      width: sourceCoords.width,
      display: 'block'
    }).adopt(this.source);
  },

  hide: function(evnt) {
    this.parent(evnt);

    // put the source element back in the page
    this.placeholder.adopt(this.source).setStyles({
      width: 'auto',
      height: 'auto'
    });
    this.popout.setStyle('display', 'none');
  },

  /**
   * Positions the container.
   */
  position: function() {
    var sourceCoords = this.source.getCoordinates();
    this.container.setStyles({
      left: null,
      top: sourceCoords.top + this.options.offset.y,
      right: window.getWidth() - (sourceCoords.left + this.options.offset.x)
    });
    this.positionShim();

    // make sure dialog is visible on screen
    window.scrollTo(0, sourceCoords.top + this.options.offset.y + this.options.scrollOffset);
  }
});
/**
 * @class ta.overlays.CoverOverlay
 */
ta.overlays.CoverOverlay = ta.overlays.Overlay.extend({
  options: {
    autoShow: true,
    xOff: 0,
    yOff: 0
  },

  initialize: function(options, elmt)
  {
    this.parent(options, elmt);

    // register for events
    if (this.options.showCloseButton) {
      this.closeBtn.addEvent('click', this.hideHandler);
    }

    if (this.options.autoShow) this.show();
  },

  position: function()
  {
    var c = this.source.getCoordinates();
    var s = this.container.getCoordinates();
    this.container.setStyles({
      left: c.left + (c.width / 2 - s.width / 2) + this.options.xOff,
      top: c.top + (c.height / 2 - s.height / 2) + this.options.yOff
    });
  },

  /**
   * update backdrop on show
   */
  updateBackdrop: function() {
    var c = this.source.getCoordinates();
    this.backdrop.setStyles({
      left: c.left,
      top: c.top,
      width: c.width,
      height: c.height
    });
  }
});

/** @namespace */

if(!ta.popups)
{
  ta.popups = {};
}

ta.popups.PopUtil = {
  windowType: {
    CHECK_RATE: 0,
    PROV_WIN: 1,
    HOLDING: 2,
    COMMERCE: 3
  },
  taPopunderSlots: null,
  taPopupSlots: null,
  taHoldingWindowCap: 2,
  taPopWindowSet : {},
  taWindowCount: 0,

  getAndUpWindowCount: function(){
    return this.taWindowCount++;
  },

  retrieveTAWindow: function(windowName){
    return this.taPopWindowSet[windowName];
  },
  
  tryTwoHoldingWindows: function(trigger)
  {
    try {
      var oneWindow = this.launchHoldingWindow(trigger);
      var twoWindow = this.launchHoldingWindow(trigger);
      if(oneWindow.windowObject && twoWindow.windowObject)
      {
        return true;
      }
    } catch(e) { 
      ta.util.error.record(e, 'try two hw', trigger); 
    }
    return false;
  },
  
  getPopunderSlots: function()
  {
    if(this.taPopunderSlots === null)
    {
      this.taPopunderSlots = new ta.popups.PopunderSlotSet();
    }
    return this.taPopunderSlots;
  },
  
  getPopupSlots: function()
  {
    if(this.taPopupSlots === null)
    {
      this.taPopupSlots = new ta.popups.PopupSlotSet();
    }
    return this.taPopupSlots;
  },

  createCheckRatesWindow: function(windowOptions){
    return this.buildNewWindow(this.windowType.CHECK_RATE, windowOptions); 
  },

  createHoldingWindow: function(windowOptions){
    return this.buildNewWindow(this.windowType.HOLDING, windowOptions);
  },

  buildNewWindow: function(winType, windowOptions){
    var newWin = new ta.popups.TAPopWindow(winType, windowOptions);
    if(!newWin)
    {
      return false;
    }
    if(!newWin.errorOpening && !newWin.errorMoving && newWin.windowObject)
    {
      this.taPopWindowSet[newWin.name] = newWin;
      return newWin;
    }
    return false; 
  },

  getHoldingWindowSet: function(){
    var hwSet = [];
    for (var i in this.taPopWindowSet)
    {
      var taWin = this.taPopWindowSet[i];
      if(taWin && taWin.type == this.windowType.HOLDING)
      {
        var holdWinObj = taWin.windowObject;
        if(holdWinObj && "closed" in holdWinObj && !holdWinObj.closed)
        {
          hwSet[hwSet.length] = taWin;
        }
      }
    }
    return hwSet;
  },

  attemptHoldingWindow: function(elmt)
  {
    if(this.taHoldingWindowCap > this.getHoldingWindowSet().length)
    {
      this.launchHoldingWindow(elmt);
    }
  },
  
  launchHoldingWindow: function(elmt)
  {
    if(elmt.className.match(/popEnabled/) || elmt.id.match(/CHECK_RATE/))
    {
      var vendorSetId = elmt.id.match(/check\w+_(\d+)/);
      if(!vendorSetId)
      {
        vendorSetId = elmt.className.match(/id_(\d+)/);
      }
      if(vendorSetId && vendorSetId[1])
      {
        vendorSetId = vendorSetId[1];
      }
      else
      {
        return false;
      }
      var hwURI = ta.retrieve("checkrates.holdingWindowURI");
      if($defined(hwURI))
      {
        var winOps = { url: hwURI };
        return this.createHoldingWindow(winOps);
      }
    }
    return false;
  },
  
  popunderReminder: function(pop_hook){
    var winArgs = { windowHook: pop_hook };
    this.createHoldingWindow(winArgs);
  }
};
  
  

/**
 * TAPopWindow class implementation
 * Description: Standard way of opening a popup or popunder window
 * Dependencies: PopUtil.js
 * Author: jbriedis
 * Date: 26.02.2010
 */

ta.popups.TAPopWindow = new Class({
  
  options: {
    name: 'TAWin',
    url: '',
    windowHook: false,
    customWidth: false,
    toolbar: 1,
    location: 1,
    directories: 1,
    status: 1,
    menubar: 1,
    resizable: 1,
    copyhistory: 1,
    scrollbars: 1,
    alwaysRaised: false,
    alwaysLowered: false
  },
  
  initialize: function(type, options)
  {
    this.setOptions(options);
    this.type = type;
    this.assignSlot();
    //assignSlot sets the default width,
    //customize here
    if(this.options.customWidth)
    {
      this.width = this.options.customWidth;
    }

    if(this.options.windowHook)
    {
      //holding window re-assignment after new page load
      this.windowObject = this.options.windowHook;
      this.name = this.windowObject.name;
      this.windowObject.refresh();
      return true;
    }

    this.name = this.options.name;
    if(this.name == 'TAWin')
    {
      this.name += ta.popups.PopUtil.getAndUpWindowCount();
    }
    this.url = this.options.url;
    try {
      this.assignWinArgs();
      this.windowObject = window.open(this.url, this.name, this.winArgs);
    }
    catch(e) {
      this.errorOpening = e;
      ta.util.error.record(this.errorOpening, 'pop window opening', null, { type: this.type, name: this.name, url: this.url});  
    }

    try {
      var winObj = this.windowObject;
      if(winObj && this.isTAWindow())
      {  
        if(this.type == ta.popups.PopUtil.windowType.HOLDING)
        {
          this.moveBehindMain(winObj);
        }
      }
    }
    catch(e) {
      this.errorMoving = e;
      ta.util.error.record(this.errorMoving, 'popunder window moving', null, { type: this.type, name: this.name, url: this.url}); 
    }
  },

  repurpose: function(newUrl, customWidth) {
    if(this.type == ta.popups.PopUtil.windowType.HOLDING)
    {
      try{
        //change type from holding
        this.type = ta.popups.PopUtil.windowType.CHECK_RATE;
        //free old popunder slot
        this.slot.occupied = false;
        this.assignSlot();
        this.url = newUrl;
        if(customWidth)
        {
          this.width = customWidth;
        }
        var winObj = this.windowObject;
        winObj.resizeTo(this.width, this.height);
        winObj.moveTo(this.x, this.y);
        winObj.focus();
        // - remove below per bug 46641
        /*
        if(!window.opener)
        {
          window.blur();
        }
        */
        winObj.location = this.url;
        
      }
      catch(e)
      {
        ta.util.error.record(e, 'popunder repurpose');  
      }
      return this;
    }
    return false;
  },

  assignWinArgs: function()
  {
    this.winArgs = "toolbar=" + this.options.toolbar;
    this.winArgs += ",location=" + this.options.location;
    this.winArgs += ",directories=" + this.options.directories;
    this.winArgs += ",status=" + this.options.status;
    this.winArgs += ",menubar=" + this.options.menubar;
    this.winArgs += ",resizable=" + this.options.resizable;
    this.winArgs += ",copyhistory=" + this.options.copyhistory;
    this.winArgs += ",scrollbars=" + this.options.scrollbars;
    this.winArgs += ",width=" + this.width;
    this.winArgs += ",height=" + this.height;
    this.winArgs += ",left=" + this.x;
    this.winArgs += ",top=" + this.y;
    this.winArgs += ",screenx=" + this.x;
    this.winArgs += ",screeny=" + this.y;
    if(this.options.alwaysRaised)         
    {
      this.winArgs += ",alwaysRaised";
    }
    if(this.options.alwaysLowered)
    {
      this.winArgs += ",alwaysLowered";
    } 
  },

  assignSlot: function() {
    if(this.type == ta.popups.PopUtil.windowType.HOLDING)
    {
      this.slot = ta.popups.PopUtil.getPopunderSlots().getOpenSlot();
      this.width = this.slot.tw;
      this.height = this.slot.th;
      this.x = this.slot.tx;
      this.y = this.slot.ty;
    }
    else
    {
      this.slot = ta.popups.PopUtil.getPopupSlots().getOpenSlot();
      this.width = this.slot.w;
      this.height = this.slot.h;
      this.x = this.slot.x;
      this.y = this.slot.y; 
    }
    this.slot.occupied = this;
  },

  moveBehindMain: function(winHook) {
    winHook.blur();
    winHook.opener = self;
    winHook.moveTo(this.slot.x, this.slot.y);
    winHook.resizeTo(this.slot.w, this.slot.h);
    winHook.location = this.url;
    window.focus();
  },

  isTAWindow: function(){
    var isTA = true;
    try {
      var tempLoc = this.windowObject.location;
      // for some reason IE runs the next line even if the last one throws a SecurityException
      this.windowObject.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;
  }
});  

ta.popups.TAPopWindow.implement(new Options);
  
  

ta.popups.Slot = new Class({
  initialize: function(lx, ly, lw, lh) {
    this.x = lx;
    this.y = ly;
    this.w = lw;
    this.h = lh;
    this.occupied = null;
  }
});

ta.popups.SlotSet = new Class({
  
  initialize: function() {
    this.nW = 525;
    this.nH = 460;
    this.nXI = 24;
    this.nYI = 24;
    this.totalSlots = 20;
    this.slots = [];

    try{
      if(screen.width > 1024){
        this.nW = 800;
        this.nH = 600;
        this.nXI = 80;
        this.nYI = 40;
      } else if(screen.width > 800){
        this.nW = 620;
        this.nH = 500;
        this.nXI = 60;
        this.nYI = 30;
      }
    }
    catch(e)
    {
      this.error = e;
      ta.util.error.record(this.error, 'slot set', null, { 'slot set': 'general'}); 
    }
  },
  
  getUsedCount: function()
  {
    var usedSlots = 0;
    if(this.slots)
    {
      for(var j = 0; j < this.slots.length; j++)
      {
        if(this.slots[j].occupied && !this.slots[j].occupied.closed)
        {
          usedSlots++;
        }
      }
    }
    return usedSlots;
  },
  
  getOpenSlot: function (n){
    if(!n || n < 0 || n > this.totalSlots)
    {
      n = 0;
    }
    var getPopSlot = function(slotNum) {
      try {
        var taWin = this.slots[slotNum].occupied;
        var doAssign = false;
        if(!taWin)
        {
          doAssign = true;
        }
        else {
          var winObj = taWin.windowObject;
          if(!winObj || winObj.closed)
          {
            doAssign = true;
          }
        }
      }
      catch(e){
        ta.util.error.record(e, 'get open slot - get pop slot', null, { totalSlots: this.totalSlots, slotNumber: slotNum});   
      }
      if(doAssign)
      {
        this.slots[slotNum].occupied = null; 
        var retSlot = this.slots[slotNum];
        retSlot.slotPos = slotNum;
        return retSlot; 
      }
      return false;
    }.bind(this);
    try{
      for (var i = n - 1; i >= 0; i -= 1){
        var newSlot = getPopSlot(i);
        if(newSlot)
        {
          return newSlot;
        }
      }
      for (var i = n; i < this.slots.length; i++){
        var newSlot = getPopSlot(i);
        if(newSlot)
        {
          return newSlot;
        }   
      }
    }
    catch(e){
      ta.util.error.record(e, 'get open slot', null, { totalSlots: this.totalSlots});  
    }
    return false;
  }
});


ta.popups.PopunderSlot = ta.popups.Slot.extend({
  initialize: function (x, y, w, h, tx, ty, tw, th){
    this.parent(x, y, w, h);
    this.tx = tx;
    this.ty = ty;
    this.tw = tw;
    this.th = th;
  }
});
  
ta.popups.PopupSlotSet = ta.popups.SlotSet.extend({
  initialize: function (){
    this.parent();
    this.nX = 500;
    try{
      this.nY = (window.screenTop || window.screenY);
    }
    catch(e)
    {
      this.error = e;
      this.nY = 0;
      ta.util.error.record(this.error, 'slot set', null, { 'slot set': 'popup'});
    }
    for (var i = 0; i < this.totalSlots; i++){
      this.slots[i] = new ta.popups.Slot(this.nX, this.nY, this.nW, this.nH);
      this.nX = this.nX + this.nXI;
      this.nY = this.nY + this.nYI;
    }
  }
});

ta.popups.PopunderSlotSet = ta.popups.PopupSlotSet.extend({
  
  initialize: function() {
    this.parent();
    try{
      this.nX = (window.screenLeft || window.screenX);
      this.nY = (window.screenTop || window.screenY); 
    }
    catch(e)
    {
      this.error = e;
      this.nX = 200;
      this.nY = 200;
    }
    this.w = 1;
    this.h = 1;
    this.x = 5000;
    this.y = 5000;
    this.totalSlots = 4;
    try{  
      if (window.ie7) {this.w = 250; this.h = 100;}
      if (window.webkit) { this.w = 85; this.h = 100;}
    
      if (typeof(this.nX) == "undefined") { // full screen
        this.nX = 0;
        this.nY = 0;
        this.x = this.nX + window.screen.availWidth - this.w; // bottom-right corner of window
        this.y = this.nY + window.screen.availHeight - this.h;
      }
      else{
        this.x = this.nX + window.getWidth() - this.w; // bottom-right corner of window
        this.y = this.nY + window.getHeight() - this.h;
      }
      if(window.ie7)
      {
        this.y = this.y - 95;
        this.x = this.x + 13;
      }
      else if(window.webkit)
      { 
        this.y = this.y - 16;
      }
    }
    catch(e)
    {
      //catch errors related to window references
      this.error = e;
    }
    if(this.error)
    {
      ta.util.error.record(this.error, 'slot set', null, { 'slot set': 'popunder'});
    }
    for (var i = 0; i < this.totalSlots; i++){
      this.slots[i] = new ta.popups.PopunderSlot(this.nX, this.nY, this.nW, this.nH, this.x, this.y, this.w, this.h);
      this.nX = this.nX + this.nXI;
      this.nY = this.nY + this.nYI;
    }
  }
  
});
ta.commerce.Vendor = new Class({
  options: {
    checkboxID: false,
    additionalOffer: false,
    defaultChecked: false
  },
  
  /**
   * @param {Object} options Options, see below.
   */
  initialize: function(options, vName, vUrl, vVendorName, vPropertyId) {
    this.setOptions(options); 
    this.name = vName;
    this.url = vUrl;
    this.window = null;
    this.slot = null;
    this.opened = false;
    this.vendorName = vVendorName;
    this.propertyId = vPropertyId;
    this.isFlight = false;
  },

  isChecked: function() {
    if(this.options.checkboxID)
    {
      var checkboxObject = $(this.options.checkboxID);
      if(!checkboxObject)
      {
        checkboxObject = $(this.options.checkboxID + "_pop");
      }
      if(checkboxObject)
      {
        return checkboxObject.checked;
      }
    }
    return this.options.defaultChecked;
  },

  isAvailable: function() {
    if(!this.isOpened())
    {
      return true;
    }
    return false;
  },

  isOpened: function() {
    try{
      if(!this.opened || !this.hasWindow())
      {
        return false;
      }
    }
    catch(e)
    {
      //TODO::log any errors related to window issues
    }
    return true;
  },

  hasWindow: function() {
    if(!this.window || !this.window.windowObject || !("closed" in this.window.windowObject) || this.window.windowObject.closed)
    {
      return false;
    }
    return true;
  },

  getUrl: function(crData) {
    var winUrl = this.url;

    if (this.isFlight) {
      winUrl += '&' + crData.getFlightData();
    } else if (crData.type != 'checkdeals') {
      winUrl += '&' + crData.getUserData();
      
      //set custom commerce area
      var customArea = "QC_Inline";
      var crTrigger = crData.trigger;
      if(crTrigger)
      {
        var customAreas = false;
        if(customAreas = crTrigger.className.match(/custom_area_(\w+)/))
        {
          customArea = customAreas[1];
        }
      }
      winUrl = ta.commerce.checkrates.addCustomUrlParams(winUrl, customArea);
    }
    return winUrl;
  },

  errorString: function() {
    var errStr = [];
    if(this.name)
    {
      errStr.push('&name_');
      errStr.push(this.name);
    }
    if(this.slot)
    {
      errStr.push('&slot_full');
    }
    if(this.propertyId)
    {
      errStr.push('&propId_');
      errStr.push(this.propertyId);
    }
    var str = errStr.join('');
    if(str && str[0])
    {
      return str.slice(1);
    }
    return "noInfo";
  }
});
ta.commerce.Vendor.implement(new Options); 
ta.commerce.VendorSet = new Class({
  options: {
    vendorSet: false
  },     

  initialize: function(options, vendSetId) {
    this.setOptions(options);  
    this.vendorSetId = vendSetId;
    if(this.options.vendorSet)
    {
      this.vendors = this.options.vendorSet;
    }
  },

  getVendors: function() {
    if(!this.vendors)
    {
      this.vendors = ta.retrieve("checkrates.vendors." + this.vendorSetId); 
    }
    return this.vendors;
  },

  getCheckedInfo: function(what) {
    var numChecked = 0;
    var openCount = 0;
    var vcString = [];
    var voString = [];
    try{
      var vendorSet = this.getVendors();
      for (var i = 0; i < vendorSet.length; i++)
      {
        var aVendor = vendorSet[i];
        if (aVendor.isChecked())
        {
          numChecked++;
          vcString.push(aVendor.name);
          vcString.push('_');
        } 
        if (aVendor.window && aVendor.window.closed)
        {
          aVendor.window = null;
        }  
        else if(aVendor.isOpened())
        {
          openCount++;
          voString.push(aVendor.name);
          voString.push('_'); 
        }
      }
      if(vcString.length > 0)
      {
        vcString.pop();
      }
      if(voString.length > 0)
      {
        voString.pop();
      }
      if(what == "count")
      {
        return numChecked; 
      }
      else if(what == "tpw_string"){
        return vcString.join('');
      }
      else if(what == "open_count"){
        return openCount;
      }
      else if(what == "check_more"){
        return {
          vendorsChecked: vcString.join(''),
          vendorsOpened: voString.join('')
        };
      }
    }
    catch(e){
     //TODO:: log any issues related to this function
    }
    return {
      checked: numChecked, 
      tpw_string: vcString.join(''), 
      open: openCount, 
      tpw_open_string: voString.join('')
    };
  },

  getCheckedCount: function() {
    return this.getCheckedInfo('count');
  },

  getCheckedString: function() {
    return this.getCheckedInfo('tpw_string');  
  },

  oneChecked: function() {
    var numChecked = this.getCheckedCount();
    if (numChecked == 0)
    {
      alert(js_0001); // "Please select at least one vendor from the list."
      return false;
    }  
    return true;
  },

  getOpenCount: function() {
    return this.getCheckedInfo('open_count'); 
  },
  
  storeCheckBoxValues: function() {
    var checkBoxSet = {};
    this.getVendors().each(function(aVendor){
      checkBoxSet[aVendor.name] = aVendor.isChecked();
    });
    ta.store('checkBoxSet_' + this.vendorSetId, checkBoxSet);
  },

  errorString: function() {
    var errStr = [];
    var vendors = this.getVendors();
    if(!vendors || vendors.length < 1)
    {
      return "empty.vendor.set";
    }
    errStr.push("VSet::");
    vendors.each(function(aVendor, index){
      errStr.push("V");
      errStr.push(index);
      errStr.push(":");
      errStr.push(aVendor.errorString());
    });
    return errStr.join('');
  }
});
ta.commerce.VendorSet.implement(new Options);  

ta.commerce.CheckRatesData = new Class({
  /**
   * @param {Object} options Options.
   * type: type of check rates click - determines user flow
   * evnt: event associated with check rates click
   */ 
  options: {
    type: 'checkRates',
    evnt: false
  },
  
  /**
   * @param {Object} options Options, see below.
   */
  initialize: function(options, crTrigger, crVendorSetId) {
    this.setOptions(options); 
    this.trigger = crTrigger;
    this.type = this.options.type;
    this.vendorSetId = crVendorSetId;
    
    if(this.type == 'geocheckrates')
    {
      var geoLocMatch = false;
      if(geoLocMatch = crTrigger.className.match(/locId_(\d+)/))
      {
        this.geoLocId = geoLocMatch[1];
      }
    }
    if(this.vendorSetId)
    {
      this.vendorSet = new ta.commerce.VendorSet({}, this.vendorSetId); 
    }
    if(this.trigger)
    {
      var mooTrigger = $(this.trigger);
      this.formElem = mooTrigger.getParent('form');
      if(this.formElem)
      {
        this.infoField = this.formElem.getElement('span.error_msg');
      }
    }
  },

  showInfo: function(infoText) {
    if(this.infoField)
    {
      this.infoField.setContent(infoText).show();
    }
  },

  hideInfo: function() {
    if(this.infoField)
    {
      this.infoField.hide(); 
    }
  },
  
  validate: function() {
    if(!this.trigger ||
      !this.vendorSetId ||
      !this.getVendorSet() ||
      !this.getVendorSet().getVendors() ||
      !this.getVendorSet().oneChecked() ||
      !this.validateDates())
    {
      try{
        var vendErr = "NoVendSet";
        if(this.getVendorSet())
        {
          vendErr = this.getVendorSet().errorString();
        }
        var errTarg = "NoTarg";
        if(this.target)
        {
          errTarg = ta.util.error.getSelectorString(this.target);
        }
        ta.util.error.record(this.options.evnt, 'cr data validate', this.options.evnt, {target: errTarg, vendorSetId: this.vendorSetId, vendorSet: vendErr}); 
      }
      catch(e){
      }
      return false;
    }                     
    return true;
  },

  getVendorSet: function() {
    if(this.reorderedVendorSet)
    {
      return this.reorderedVendorSet;
    }
    return this.vendorSet;
  },

  validateDates: function() {
    var checkInElem = $('checkIn_' + this.vendorSetId);
    var checkOutElem = $('checkOut_' + this.vendorSetId);
  
    // doesn't have form or date fields, can't do any further processing.
    if (!this.formElem || !checkInElem || !checkOutElem || !checkInElem.value || !checkOutElem.value)
    {
      if(!checkInElem)
      {
        checkInElem = $('checkIn_' + this.vendorSetId + "_pop");
        checkOutElem = $('checkOut_' + this.vendorSetId + "_pop");
      }
      
      if (!this.formElem || !checkInElem || !checkOutElem || !checkInElem.value || !checkOutElem.value)
      {
        return true;
      }
    }
    
    this.inDate = this.parseDate(checkInElem.value);
    this.outDate = this.parseDate(checkOutElem.value);
    
    this.errorMsg = this.dateErrorCheck(this.inDate);
    if(this.errorMsg)
    {
      this.showInfo(this.errorMsg);
      return false;
    }
    
    this.errorMsg = this.dateErrorCheck(this.outDate);
    if(this.errorMsg)
    {
      this.showInfo(this.errorMsg);  
      return false;
    }
    
    if (this.outDate.getTime() < this.inDate.getTime()) {
      if(!js_0003)
      {
        js_0003 = " ";
      }
      this.showInfo(js_0003);  
      return false;
    }
    return true;
  },           
  
  parseDate: function(dateVal) {
    var userDate = false;
    if(DATE_FORMAT && dateVal)
    {
      var r = dateVal.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 (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;
          userDate = new Date(nm + "/" + nd + "/" + ny);
        }
      }
    }
    return userDate;
  },
  
  /*
   * dateErrorCheck checks to make sure that the date entered can be parsed,
   * is in the future and is not more than 330 days in the future
   * dateVal: js Date object
   * returns false if date is validated, an error string if date is not valid
   */
  dateErrorCheck: function(dateVal) {
    if (!dateVal || isNaN(dateVal))
    {
      if(js_invalid_dates_text)
      {
        return js_invalid_dates_text;
      }
      return " ";
    }
    
    var todaysDate = new Date();
    todaysDate.setHours(0,0,0,0);
    var todaysDatePlusMax = new Date(todaysDate.getFullYear(), todaysDate.getMonth(), todaysDate.getDate()+330);
    if(dateVal.getTime() < todaysDate.getTime())
    {
      if(js_0002)
      {
        return js_0002; // "Please choose dates in the future."
      }
      return " ";
    }
    if(todaysDatePlusMax.getTime() < dateVal.getTime())
    {
      if(js_0004)
      {
        return js_0004;
      }
      return " ";
    }
    return false;
  },

  getUserData: function()
  {
    if(!this.userData)
    {
      var vendSetId = this.vendorSetId;
      var inMonthElem = $('qcInMonth_' + vendSetId);
      if(!inMonthElem)
      {
        vendSetId = this.vendorSetId + "_pop";
        inMonthElem = $('qcInMonth_' + vendSetId);
      }
      if(!inMonthElem)
      {
        //TODO:: shouldn't get here, log this?
        return false;
      }
    
      var outMonthElem = $('qcOutMonth_'+vendSetId);
      var inDayElem = $('qcInDay_'+vendSetId);
      var outDayElem = $('qcOutDay_'+vendSetId);
      var adultsElem = $('qcAdults_'+vendSetId);

      var sInMonthYear = inMonthElem.value.split(/\//);
      var sOutMonthYear = outMonthElem.value.split(/\//);
    
      var dCheckin = new Date( sInMonthYear[1], sInMonthYear[0]-1 , inDayElem.value);
      var dCheckout = new Date(sOutMonthYear[1], sOutMonthYear[0]-1, outDayElem.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.getTime() < dToday.getTime() || dCheckout.getTime() < dToday.getTime()) {
        alert(js_0002); // "Please choose dates in the future."
        return false;
      }
  
      if (dCheckin.getTime() >= dCheckout.getTime()) {
        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.getTime() > dTooFar.getTime() || dCheckout.getTime() > dTooFar.getTime()) {
        alert(js_0004); // "Please choose dates that are less than 330 days away."
        return false;
      }
  
      var buffer = [];
      buffer.push('inMonth=');
      buffer.push(sInMonthYear[0]);
      buffer.push("%20");
      buffer.push(sInMonthYear[1]);
      buffer.push("&inDay=");
      buffer.push(escape(inDayElem.value));
      buffer.push("&outMonth=");
      buffer.push(sOutMonthYear[0]);
      buffer.push("%20");
      buffer.push(sOutMonthYear[1]);
      buffer.push("&outDay=");
      buffer.push(escape(outDayElem.value));
      buffer.push("&adults=");
      buffer.push(adultsElem.selectedIndex+1);
    
      var pidElmt = $('hacPid');
      if(pidElmt && pidElmt.value)
      {
        buffer.push("&pid=");
        buffer.push(pidElmt.value);
      } 
      this.userData = buffer.join('');    
    }
    return this.userData;
  },

  getFlightData: function() {
    var vendSetId = this.vendorSetId;
    var inMonthElem = $('qcInMonth_' + vendSetId);
    if(!inMonthElem)
    {
      vendSetId = this.vendorSetId + "_pop";
      inMonthElem = $('qcInMonth_' + vendSetId);
    }
    if(!inMonthElem)
    {
      return false;
    }
    
    var outMonthElem = $('qcOutMonth_'+vendSetId);
    var inDayElem = $('qcInDay_'+vendSetId);
    var outDayElem = $('qcOutDay_'+vendSetId);

    var sInMonthYear = inMonthElem.value.split(/\//);
    var sOutMonthYear = outMonthElem.value.split(/\//);
   
    var buffer = [];
    buffer.push('date0=');
    buffer.push(sInMonthYear[1]);
    buffer.push(sInMonthYear[0]);
    buffer.push(escape(inDayElem.value));
    buffer.push('&date1=');
    buffer.push(sOutMonthYear[1]);
    buffer.push(sOutMonthYear[0]);
    buffer.push(escape(outDayElem.value));
    buffer.push('&pid=3253');
    return buffer.join('');    
  },

  resetVisitedFlags: function() {
    var userDataRef = "checkrates.sUserData." + this.vendorSetId;
    var oldUserData = ta.retrieve(userDataRef);
    var newUserData = this.getUserData();
    
    //reset visited flags for vendors if data has changed
    if(oldUserData && oldUserData != newUserData)
    {
      var vendors = this.getVendorSet().getVendors();
      for( x = 0; x < vendors.length; x++ ){
        vendors.opened = false;
      }
    }
    
    //store it for next check rates click
    ta.store(userDataRef, newUserData);
  },

  getCROpenCount: function(vendorSet) {
    var openCount = 0;
    vendorSet.each(function(vendObj){
      var vendWin = vendObj.window;
      if(vendObj && vendObj.bOpened && vendWin && vendWin.windowObject && !vendWin.windowObject.closed)
      {
        openCount++;
      }
    });
    return openCount;
  },            

  fixPopOrder: function() {
    
    var reorderedSet = this.getVendorSet().getVendors().copy();
    
    //strip out unchecked CR box vendors
    var tempSet = [];
    reorderedSet.map(function(vendor, index){
      if(vendor.isChecked())
      {
        tempSet[tempSet.length] = vendor;
      }
    });
    reorderedSet = tempSet;
    var hwSet = ta.popups.PopUtil.getHoldingWindowSet();
    var openCRWinCount = this.getVendorSet().getOpenCount();
    var totalOpenWin = hwSet.length + openCRWinCount;
    var checkedCount = this.getVendorSet().getCheckedCount();
    if ((totalOpenWin + 1) < checkedCount)
    {
      var success = ta.popups.PopUtil.tryTwoHoldingWindows(this.trigger);
      if(success)
      {
        //reverse all the vendors
        reorderedSet = reorderedSet.reverse();
      }
      else
      {
        //reverse the first (holding-window + 1) vendors
        var holdingArr = [];
        for(var j = 0; j < hwSet.length + 1; j++)
        {
          holdingArr.push(reorderedSet.shift());
        }
        for(var k = 0; k < holdingArr.length; k++)
        {
          reorderedSet.unshift(holdingArr[k]);
        }
      }
    } 
    else
    {
      reorderedSet = reorderedSet.reverse();
    }
    this.reorderedVendorSet = new ta.commerce.VendorSet({vendorSet: reorderedSet}, this.vendorSetId);
  }
});
ta.commerce.CheckRatesData.implement(new Options);  

/** @namespace */
ta.commerce.checkrates = {

  /**
   * Callback for when the check rates button is pressed.
   */
  checkRatesClicked: function(event, elmt, vendSetId, crType) {
    ta.commerce.checkrates.displayVendors(
      new ta.commerce.CheckRatesData({type: crType, evnt: event}, elmt, vendSetId) 
    );
  },

   /**
   * Callback for when the check deals button is pressed.
   */
  checkDealsClicked: function(event, elmt, vendSetId) {
    ta.commerce.checkrates.displayVendors(
      new ta.commerce.CheckRatesData({type: 'checkdeals', evnt: event}, elmt, vendSetId) 
    ); 
  },

  /**
   * Open a series of windows with rate information for each selected vendor 
   */
  displayVendors: function(crData) {
    
    var crOverlayElem = $E('.overlay');
    if(crOverlayElem && crOverlayElem.overlay && crOverlayElem.overlay.pin)
    {
      crOverlayElem.overlay.pin();
    }
    
    if (!crData.validate()) 
    {
      return;
    }
    
    crData.hideInfo();
    
    try {
      this.popwindows(crData);
      this.addBoomerangTag(crData);
    } catch(e) { 
      //TODO::do a general catch, log this
    }
    return false;
  },
  
  popwindows: function (crData)
  {
    crData.resetVisitedFlags();
    ta.store('checkrates.flightData', crData.getFlightData());

    var msgBox = $("msgbox"); 
    if(crData.type == 'providerWindow')
    {
      ta.commerce.commerceHelper.openProviderWindowTest(crData);
      if(js_0014 && msgBox)
      {
        msgBox.hide().empty();
      }
      return false;
    }
    
    if(js_0005 && msgBox)
    {
      msgBox.setContent(js_0005).show(); // "Searching for deals ... this may take a few moments"  
    }

    crData.fixPopOrder(); 
    this.popAll(crData);
 
    if(msgBox)
    {
      msgBox.hide().empty();
    } 
  
    return true;
  },

  popAll: function(crData)
  {
    var vendors = crData.getVendorSet().getVendors();
    var cap = vendors.length;
    if(crData.type == "alsoViewed")
    {
      var cap = 2;
    }
    else
    {
      ta.store("checkrates.avUserData", crData.getUserData()); 
      ta.store("checkrates.avId", crData.vendorSetId);
    }
    for (var i = 0; i < vendors.length; i++)
    {
      var aVendor = vendors[i];
      if(aVendor.isChecked() && aVendor.isAvailable())
      {
        if(cap < i)
        {
          this.popBlock(crData);
          return false;
        } 
        
        var successOpening = this.popOne(aVendor, crData);
        if(!successOpening)
        {
          this.popBlock(crData);
          return false;
        }
      }
    }
    
    if(crData.type != "alsoViewed" && crData.type != 'checkdeals')
    {  
      this.launchAVLBTest(crData);
    }
    return true;
  },

  popBlock: function(crData)
  {
    crData.getVendorSet().storeCheckBoxValues();
    var uData = crData.getUserData();
    var moreData = "";
    uData = ta.retrieve("checkrates.avUserData");
    var checkMoreData = crData.getVendorSet().getCheckedInfo("check_more");
    if(checkMoreData)
    {
      moreData = "&vendorsChecked=" + checkMoreData.vendorsChecked + "&vendorsOpened=" + checkMoreData.vendorsOpened;
    }
    var pageVal = pageServlet;
    if(!pageVal)
    {
      pageVal = "unknown";
    }
    
    var avLocId = "&avLocId=";
    var avId = ta.retrieve("checkrates.avId");
    if(crData.geoLocId)
    {
      avLocId += crData.geoLocId;
    }
    else if(avId)
    {
      avLocId += avId;
    }
    else
    {   
      avLocId += crData.vendorSetId;
    }

    var customArea = "QC_Button";
    var customAreas = false;
    if(customAreas = crData.trigger.className.match(/custom_area_(\w+)/))
    {
      customArea = customAreas[1];
    }   
    
    var remContent = "/CheckMore?detail=" + crData.vendorSetId + "&storedUserData=" + encodeURIComponent(uData) + moreData + "&a=" + customArea + "&s=" + pageVal + "&av=true" + avLocId;  
    
    if(crData.type == 'checkdeals') 
    {
      remContent = 'ABS:vpages/quickcheckdeals_ie.html';
    }
    
    var moreData = null;
    moreData = crData.getVendorSet().getCheckedInfo("check_more");

    var absOverlay = new ta.overlays.AbsoluteOverlay({
      style: 'alsoViewedTest',
      backdrop: ta.overlays.BACKDROP_ALWAYS
    }, crData.trigger);
    if(crData.type == 'checkdeals')
    {
      absOverlay.loadRemoteSuccess = function(txt) {
        this.inner.setHTML(txt);
        ta.commerce.smartdeals.popBlockerOverlay(absOverlay, crData);
        this.position();
      };        
    }

    absOverlay.loadRemoteContent(remContent, false);
  },

  daodaoPopOne: function(index, evt, elmt, accomId)
  {
    var crData = new ta.commerce.CheckRatesData({evnt: evt}, elmt, accomId) 
    if (!crData.validate() || index < 0) 
    {
      return;
    }    
    var vendors = crData.getVendorSet().getVendors();  
    //for daodao need every click opened in new window,
    //the trigger of window open mechanism is 'name' attribute of vendor object,
    //keep 'name' attribute empty and it works.
    vendors[index].name = '';
    ta.commerce.checkrates.popOne(vendors[index], crData);
  },
  
  popOne: function(crVendor, crData)
  {                    
    try {
      if(crVendor.hasWindow())
      {
        try{
          var vendWindow = crVendor.window; 
          vendWindow.windowObject = window.open(crVendor.getUrl(crData), vendWindow.name, vendWindow.winArgs);
          vendWindow.windowObject.focus();
        }
        catch(e)
        {
          ta.util.error.record(e, 'pop one - reuse popup', crData.trigger);    
          return false;
        }
      }
      else
      {
        var winName = crVendor.name; 
        var hotelComWidth = false;
        if(winName == "hotels.com")
        {
          hotelComWidth = 975;
        } 
      
        var holdingWinSet = ta.popups.PopUtil.getHoldingWindowSet();
        if(holdingWinSet.length > 0)
        {
          crVendor.window = holdingWinSet[holdingWinSet.length-1].repurpose(crVendor.getUrl(crData), hotelComWidth);
        }
        else
        {
          var winOps = { customWidth: hotelComWidth, name: winName, url: crVendor.getUrl(crData) }
          crVendor.window = ta.popups.PopUtil.createCheckRatesWindow(winOps);
        }
      }
    } catch(e) { 
      ta.util.error.record(e, 'pop one - general', crData.trigger);     
      return false;
    }
    if(crVendor.window && crVendor.window.windowObject)
    {
      crVendor.opened = true;
      return crVendor.window;
    }
    return false;
  },

  addBoomerangTag: function(crData) {
    var formdata = {};
    var checkInElem = $('checkIn_' + crData.vendorSetId);
    var checkOutElem = $('checkOut_' + crData.vendorSetId);
    var adultsElem = $('qcAdults_' + crData.vendorSetId);
    
    if(checkInElem && checkOutElem && adultsElem)
    {
      formdata.checkIn = checkInElem.value;
      formdata.checkOut = checkOutElem.value;
      formdata.adults = adultsElem.value;
    }
    else
    {
      return;
    }

    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();
  },

  launchAVLBTest: function(crData) {
    if (pageServlet && modelGeoId) {
      //url for also viewed content
      var locID = crData.vendorSetId;
      if(crData.type == 'geocheckrates')
      {
        locID = crData.geoLocId;
      } 
      var avUrl = 'http://' + document.domain + '/AlsoViewedLB?d=' + locID + '&s=' + pageServlet + '&geo=' + modelGeoId + "&" + crData.getUserData() + "&checkRatesType=checkRates";
      
      // display results of below ajax call
      // in an overlay
      var compFunc =  function(res){
        var pwrOverlay = new ta.overlays.CenteredOverlay({
          backdrop: ta.overlays.BACKDROP_ALWAYS, 
          style: 'alsoViewedTest avStandalone'})
          .loadRemoteSuccess(res);
        
        var hideOverlay = false;
        if(pwrOverlay.inner)
        {
          var emptyAlsoViewed = $('emptyAlsoViewed');
          if(emptyAlsoViewed)
          {
            hideOverlay = true;
          }
          else
          {
            ta.store('alsoViewedLB', true);
          }
        }
        else
        {
          hideOverlay = true;
        }
        if(hideOverlay)
        {
          pwrOverlay.hide();
          ta.store('commerce.pwrOverlay', pwrOverlay); 
        }
      };
      
      //make the ajax request for the also viewed content
      new Ajax(avUrl, {
        onComplete: compFunc,
        evalScripts: true 
      }).request();
    }
  }, 

  openProviderWindowUrl : function(proWinURL, accomId, ctryId, vendorsChecked)
  {
        var provWinNum = ta.asInt('commerce.provWinNum');
    provWinNum++;
    ta.store('commerce.provWinNum', provWinNum);
    var previousTpwNumber = ta.retrieve("commerce.previousTpwNumbers");
    var providerWindowHeirarchy = previousTpwNumber + "_" + provWinNum;
    // avoid double __
    if(!previousTpwNumber)
    {
      providerWindowHeirarchy = provWinNum;
    }
    var winName = "TAProviderWindow_" + providerWindowHeirarchy;
    proWinURL = proWinURL + "&otpwn=" + providerWindowHeirarchy;
    var providerWin = window.open(proWinURL, winName, this.getProviderWinArgs(providerWindowHeirarchy));
    providerWin.focus();
    ta.store('commerce.TPWindow', providerWin);
    var windowCheck = (function(){ 
      try {
        if(providerWin.closed)
        {
          $clear(windowCheck);
        }
        else
        {
          var loc = providerWin.location.href;
          // for some reason IE runs the next line even if the last one throws a SecurityException
          window.status = loc; // so do something pointless - DO NOT REMOVE THIS LINE!
        }
      }
      catch (e) {
        $clear(windowCheck);
        var uri = "/ActionRecord?action=fbProvWin|id_" + accomId + "|ctrId_" + ctryId + '|cv_' + vendorsChecked;
        new Ajax(uri, {onComplete: function(){/* do nothing */}}).request();
      } 
    }).periodical(1500);
    return providerWin;
  },

  addCustomUrlParams: function(winUrl, area)
  {
    var areaParam = new RegExp("(&|\\?)area=[^\&]*");
    winUrl = winUrl.replace(areaParam,"$1area="+area);

    if(crPageServlet)
    {
      var fromParam = new RegExp("(&|\\?)from=[^\&]*"); 
      winUrl = winUrl.replace(fromParam,"$1from=HotelDateSearch_"+crPageServlet);
    } 
    
    return winUrl;
  },

  checkMoreClick: function(e, elmt, winName, uncheckedStyle, checkedStyle)
  {
    new Event(e).preventDefault();
    if(elmt)
    {
      var hotelComWidth = false;
      if(winName == "hotels.com")   
      {
        hotelComWidth = 975;
      }
      var customArea = "QC_Button";
      var customAreas = false;
      if(customAreas = elmt.className.match(/custom_area_(\w+)/))
      {
        customArea = customAreas[1];
      }   
      var isFlight = $(elmt).hasClass('flightOffer');
      var winUrl;
      if (isFlight) {
        winUrl = elmt.href + '&' + ta.retrieve('checkrates.flightData');
      } else {
        winUrl = this.addCustomUrlParams(elmt.href, customArea);
      }
      var winOps = { customWidth: hotelComWidth, name: winName, url: winUrl }
      ta.popups.PopUtil.createCheckRatesWindow(winOps);
      if (isFlight) {
        $(elmt).removeClass('sprite-findflights_ylwCnr').addClass('sprite-findflights_ylwCnrChk');
      } else {
        $(elmt).removeClass(uncheckedStyle).addClass(checkedStyle);   
      }
      if($(elmt).getParent('#QUICK_CHECK_LB_PARTNERS'))
      {
        var unClickedCount = 0;
        $$('#QUICK_CHECK_LB_PARTNERS div a').each(function(elem){
          if($(elem).hasClass(uncheckedStyle))
          {
            unClickedCount++;
          }
        }, this); 
        if(unClickedCount < 1)
        {
          var curOverlay = ta.retrieve('overlays.current');
          if(curOverlay)
          {
            curOverlay.hide();
            ta.remove('overlays.current');
          }
          return;
        }
        else if(unClickedCount < 2)
        {
          var checkMoreSitesDiv = $E('.chkSitesDesc');
          var checkOneSiteDiv = $E('.oneMore');
          $(checkMoreSitesDiv).addClass('hideOne');
          $(checkOneSiteDiv).removeClass('hideOne'); 
        }
        else
        {
          var opnSitesElem = $E('.cs_ps .opnSites'); 
          opnSitesElem.innerHTML = unClickedCount;
        }
      } 
    }
  },

  /*
   * 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.checkrates.commCopyUpdate);
    }
    if(this.inner)
    {
      var uList = this.inner.getElement('ul');
      if(uList)
      {
        uList.getElements('li').each(
          function(listItem){ 
            ta.commerce.checkrates._updateInlineFormElement_CB(listItem, true);
          }
        );
      }
      var crSel = this.inner.getElement('select');
      if(crSel)
      {
        ta.commerce.checkrates._updateInlineFormElement_Sel(crSel, true);
      }
      var ciCal = this.inner.getElement('input.checkIn');
      if(ciCal)
      {
        ta.commerce.checkrates._updateInlineFormElement_In(ciCal, true);
      }
      var coCal = this.inner.getElement('input.checkOut');
      if(coCal)
      {
        ta.commerce.checkrates._updateInlineFormElement_In(coCal, true);
      }
    }
  },

  createPairedCalendar: function(evnt, elmt)
  {
    new Event(evnt).preventDefault();
    new ta.overlays.PairedCalendar(elmt, {
      validate: true,
      onUpdate: ta.commerce.checkrates.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.checkrates.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.checkrates.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);
    }
  },

  updateInlineFormElementCheckDeals: function(elmt) 
  {
      elmt = $(elmt);
      var elemTag = elmt.getTag().toLowerCase();
      if(elemTag == 'input')
      {
        this._updateInlineFormElement_CB(elmt, false);
      }
    },

  updateHiddenDateFields: function(evnt, elmt)
  {
    var userDate = this.parseDate(elmt.value);
    var errorMessage = this.dateErrorCheck(userDate);
    if(errorMessage && typeof(errorMessage) == "string")
    {
      if(errorMessage.replace(/^\s+|\s+$/g,"").length > 0)
      {
        alert(errorMessage);
      }
      return;  
    } 
    
    var elemOverlay = $(elmt).overlay;
    if(elemOverlay && elemOverlay.calendar)
    {
      elemOverlay.calendar.select(null, userDate);
    }
  },

  /**
   * 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'),
  
  /*
   * 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);
    if(inlineSelect)
    {
      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;
            }
          }
        }
      }
    }
  }
};
//
// Javascript for the Trip Advisor Media Group Sticky Footer
// 
// (C) Copyright 2009: TripAdvisor LLC, All Rights Reserved
// 
// @author:  avasilescu@tripadvisor.com
// @since:   12/06/09
// 


//  Define some variables
var nTAMGFooterHeight = 28;
var sTAMGFooterCookieName = "hideTAMGFooter";

var sFooterDivId = "tamgFooter";
var sFooterHoverDivIdStem = "tamgFooterHover";
var sFooterBackdropId = "tamgFooterBackdrop";
var sFooterOverlayId = "tamgFooterOverlay";

// TAMGFooter instance
var savedDomain = null;


//
// Javascript for Making AJAX Requests for the Trip Advisor Media Group Sticky Footer
// 
// (C) Copyright 2009: TripAdvisor LLC, All Rights Reserved
// 
// @author:  avasilescu@tripadvisor.com
// @since:   10/19/09
// 


var bRequestLock = false;
var hmLoadedHovers = {};
var hmOngoingHover = {};
var nLastHoverBId = -1;
var nHoverBoxWidth = 237;
var nHoverOffsetAdjustment = 20;
var bOverlayLoaded = false;


// Make a Cross-Domain request

function _makeXDomRequest (url, params, responseFunc)
{
  if (typeof(ajaxDomain) == "undefined")
  {
    ajaxDomain = "http://www.tripadvisor.com";
  }
  var scriptUrl = ajaxDomain + url + "&rt=JSON";
  _makeXDomRequest._receiveXDomResponse = function (data)
  {
    responseFunc(data.responseHTML);
  }
  addTFElement('javascript', scriptUrl);
}


// Show the on-hover box for a button via AJAX

function _openButtonHover (btnElement, nRequestBId)
{
  // ~ Is open already?
  if (hmOngoingHover[nRequestBId])
  {
    return;
  }
  hmOngoingHover[nRequestBId] = true;
  nLastHoverBId = nRequestBId;
  
  // ~ Was loaded before?
  if (hmLoadedHovers[nRequestBId])
  {
    // ~ Wait, check and show
    setTimeout(function ()
    {
      if (hmOngoingHover[nRequestBId])
      {
        var elem = btnElement;
        var nLeftOffset = elem.clientWidth;
        if (nLeftOffset == 0) { nLeftOffset = elem.offsetWidth; }
        nLeftOffset += elem.offsetLeft;
        do
        {
          elem = elem.parentNode;
          if (elem.offsetLeft)
          {
            nLeftOffset += elem.offsetLeft;
          }
        } while (elem.className.indexOf("tamgFooter") == -1);
        nLeftOffset -= nHoverBoxWidth;
        nLeftOffset += nHoverOffsetAdjustment;
        
        var footerHover = document.getElementById(sFooterHoverDivIdStem + nRequestBId);
        footerHover.style.left = nLeftOffset + "px";
        footerHover.style.display = "block";
      }
    }, 250);
  }
  // ~ Needs to be fetched
  else
  {
    // ~ Wait and make request
    setTimeout(function ()
    {
      if (!bRequestLock && hmOngoingHover[nRequestBId])
      {
        bRequestLock = true;
        
        var elem = btnElement;
        var nLeftOffset = elem.clientWidth;
        if (nLeftOffset == 0) { nLeftOffset = elem.offsetWidth; }
        nLeftOffset += elem.offsetLeft;
        do
        {
          elem = elem.parentNode;
          if (elem.offsetLeft)
          {
            nLeftOffset += elem.offsetLeft;
          }
        } while (elem.className.indexOf("tamgFooter") == -1);
        nLeftOffset -= nHoverBoxWidth;
        nLeftOffset += nHoverOffsetAdjustment;
        
        var url = "/TAMGFooterAjax?a=SHOW_BUTTON_HOVER&bId=" + nRequestBId + "&dom=" + savedDomain;
        
        _makeXDomRequest(url, null, function (responseText) 
        {
          var footerHover = document.getElementById(sFooterHoverDivIdStem + nRequestBId);
          footerHover.innerHTML = responseText;
          footerHover.style.left = nLeftOffset + "px";
          footerHover.style.display = "block";
          hmLoadedHovers[nRequestBId] = true;
          bRequestLock = false;
        } );
      }
    }, 250);
  }
}


// Close the on-hover box for a button only after checking the mouseout event

function _checkCloseButtonHover (btnElement, nRequestBId, event)
{
  // ~ Is the mouse outside the parent button hierarchy?
  var refElement = null;
  if (event.relatedTarget) 
  {
    refElement = event.relatedTarget;
  }
  else if (event.toElement)
  {
    refElement = event.toElement;
  }
  while (refElement && refElement != btnElement)
  {
    refElement = refElement.parentNode;
  }
  
  if (refElement != btnElement)
  {
    hmOngoingHover[nRequestBId] = false;
    
    // ~ Wait, check and close
    setTimeout(function () 
    {
      if (hmOngoingHover[nRequestBId] == false)
      {
        var footerHover = document.getElementById(sFooterHoverDivIdStem + nRequestBId);
        footerHover.style.display = "none";
      }
    }, 250);
  }
}


// Show the group overlay via AJAX

function _openOverlay ()
{
  if (!bRequestLock && !bOverlayLoaded)
  {
    bRequestLock = true;
    
    var url = "/TAMGFooterAjax?a=SHOW_GROUP_OVERLAY&dom=" + savedDomain;
      
    _makeXDomRequest(url, null, function (responseText) 
    {
      var footerBackdrop = document.getElementById(sFooterBackdropId);
      footerBackdrop.style.display = "block";
        
      var footerOverlay = document.getElementById(sFooterOverlayId);
      footerOverlay.innerHTML = responseText;
      footerOverlay.style.display = "block";
        
      bRequestLock = false;
      bOverlayLoaded = true;
    } );
  }
  else if (bOverlayLoaded)
  {
    var footerBackdrop = document.getElementById(sFooterBackdropId);
    footerBackdrop.style.display = "block";
        
    var footerOverlay = document.getElementById(sFooterOverlayId);
    footerOverlay.style.display = "block";
  }
}


// Close the group overlay

function _closeOverlay ()
{
  var footerBackdrop = document.getElementById(sFooterBackdropId);
  footerBackdrop.style.display = "none";
      
  var footerOverlay = document.getElementById(sFooterOverlayId);
  footerOverlay.style.display = "none";
}


//
// Javascript for Other Runtime Uses of the Trip Advisor Media Group Sticky Footer
// 
// (C) Copyright 2009: TripAdvisor LLC, All Rights Reserved
// 
// @author:  avasilescu@tripadvisor.com
// @since:   10/21/09
// 


// Function to open the links from the footer

function openUrl (url, pid, mcid, openInNewWindow, coAbbreviation)
{
  if (url.indexOf('/') == 0)
  {
    url = document.domain + url;
  }
  if (typeof(ajaxDomain) == "undefined")
  {
    ajaxDomain = "http://www.tripadvisor.com";
  }
  var targetUrl = ajaxDomain + "/TAMGFooterController?url=" + escape(url);
  if (typeof(pid) != "undefined" && pid > -1)
  {
    targetUrl = targetUrl + "&pid=" + pid;
  }
  if (typeof(mcid) != "undefined" && mcid > -1)
  {
    targetUrl = targetUrl + "&mcid=" + mcid;
  }
  
  if (openInNewWindow)
  {
    var openedPage = window.open(targetUrl);
    openedPage.opener = null;
  }
  else
  {
    window.top.location = targetUrl;
  }
  
  if (nLastHoverBId != -1)
  {
    hmOngoingHover[nLastHoverBId] = false;
    var footerHover = document.getElementById(sFooterHoverDivIdStem + nLastHoverBId);
    footerHover.style.display = "none";
  }
  return false;
}


//
// Javascript for cookie manipulation
// 
// Inspirational source: http://techpatterns.com/downloads/javascript_cookies.php
// 


// Set the TAMGFooter cookie, valid for 7 days
//   and then hide the footer

function setTAMGFooterCookie (value)
{
  // set time - a week from now
  var today = new Date();
  today.setTime( today.getTime() );
  var duration = 1000 * 60 * 60 * 24 * 7;
  var expireDate = new Date( today.getTime() + (duration) );
  
  document.cookie = sTAMGFooterCookieName + "=" + escape(value) +
                    ";expires=" + expireDate.toGMTString() + 
                    ";path=\/" + 
                    ( ( savedDomain ) ? ";domain=" + savedDomain : "" );
                    
  hideFooter();
}


// Get the TAMGFooter cookie, if it exists

function getTAMGFooterCookie () 
{
  var a_all_cookies = document.cookie.split( ';' );
  var a_temp_cookie = '';
  var cookie_name = '';
  var cookie_value = '';
  var b_cookie_found = false;

  for ( i = 0; i < a_all_cookies.length; i++ )
  {
    a_temp_cookie = a_all_cookies[i].split( '=' );
    cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

    if ( cookie_name == sTAMGFooterCookieName )
    {
      b_cookie_found = true;
      if ( a_temp_cookie.length > 1 )
      {
        cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
      }
      return cookie_value;
      break;
    }
    a_temp_cookie = null;
    cookie_name = '';
  }
  if ( !b_cookie_found )
  {
    return null;
  }
}


//
// Javascript for Loading the Trip Advisor Media Group Sticky Footer
// 
// (C) Copyright 2009: TripAdvisor LLC, All Rights Reserved
// 
// @author:  avasilescu@tripadvisor.com
// @since:   09/29/09
// 


//  Check to make sure the footer should be displayed
//    - do not allow IE6
//    - do not allow popups, but allow pages opened after following an ad

function showTAMGFooter ()
{
  if (window.ActiveXObject && !window.XMLHttpRequest) { return false; }
  if (window.top && window.top.opener) 
  { 
    try
    {
      var openerLH = window.top.opener.location.href;
      if (openerLH.indexOf("ad.doubleclick.net") != -1) { return true; }
      if (openerLH.indexOf(savedDomain) != -1) { return false; }
    }
    catch(err) { return true; } 
  }
  
  return true;
}


//  Add an element to the document head as needed for initialization

function addTFElement(elemType, elemContent)
{
  var domElement = null;
  if (elemType == 'javascript')
  {
    domElement = document.createElement('script');
    domElement.type = 'text/javascript';
    domElement.src = elemContent;
  }
  else if (elemType == 'css')
  {
    domElement = document.createElement('link');
    domElement.rel = 'stylesheet';
    domElement.type = 'text/css';
    domElement.media = 'screen';
    domElement.href = elemContent;
  }
  
  if (domElement != null)
  {
    document.head.appendChild(domElement);
  }
}


//  Add bottom margin to the body element to make sure that the footer doesn't
//    cover an important portion of the screen

function addTFBottomMargin()
{
  var addMargin = nTAMGFooterHeight;
  var botMargin = document.body.style.marginBottom;
  if (botMargin == '' || botMargin == botMarginPx) { botMargin = 0; }
  else { botMargin = botMargin.substring(0, botMargin.length - 2); botMargin = parseInt(botMargin); }
  botMargin += addMargin;
  var botMarginPx = botMargin + 'px';
  document.body.style.marginBottom = botMarginPx;
}


//  Hide the footer on the page (when the x is pressed)

function hideFooter ()
{
  var footerElem = document.getElementById(sFooterDivId);
  if (footerElem) { footerElem.style.display = "none"; }

  //hide any takeovers
  var tkOvrDiv = $('FTR_TO_DIV');
  if(tkOvrDiv) { tkOvrDiv.style.display = "none"; } 
}


//  Add the loading of the Trip Advisor Media Group sticky footer
//    after page load occurs.

function TAMGFooterStartUp ()
{
  // ~ Establish the domain
  var pageDomain = document.domain;
  var domainPieces = pageDomain.split(".");
  if (domainPieces.length > 3 && domainPieces[domainPieces.length - 2] == "co")
  {
    savedDomain = domainPieces[domainPieces.length - 3] + "." + 
                 domainPieces[domainPieces.length - 2] + "." +
                 domainPieces[domainPieces.length - 1];
  }
  else if (domainPieces.length > 2)
  {
    savedDomain = domainPieces[domainPieces.length - 2] + "." + 
                 domainPieces[domainPieces.length - 1];
  }
  else
  {
    savedDomain = pageDomain;
  }
  
  // ~ Check special situations when not to display
  if (!showTAMGFooter()) { return; }
  
  // ~ Check the cookie
  if (getTAMGFooterCookie()) { return; }
  
  // ~ We may need to draw the footer (if delivered through JS)
  if(typeof TAMGFooterDraw == 'function') { TAMGFooterDraw(); }
  
  // ~ Accommodate the footer on the parent page
  addTFBottomMargin();
}

if ( window.addEventListener ) { window.addEventListener("load", function () { setTimeout(TAMGFooterStartUp, 1); }, false); } 
else if ( window.attachEvent ) { window.attachEvent("onload", function () { setTimeout(TAMGFooterStartUp, 1); }); }


/** @namespace */
/**
 * Namespace dependencies include: none
 */
ta.util.error = {
  /**
   * recordError writes js errors to the access logs via JSErrorRecord servlet
   * error: the js error object
   * errorBlock: the function or block of code in which the error occurred
   * event: the event object that is associated with the error block (optional)
   * extraInfo: extra info of type object that can be serialized (optional)
   * * extraInfo should not have any nested objects, it should only contain
   *   primitive types as key values 
   */
  record: function(error, errorBlock, evnt, extraInfo) {
    if (!IS_DEBUG && (!serverPool || serverPool != "R"))
    {
      return;
    }
    var eTargetSel = "";
    if(evnt || window.event)
    {
      evnt = new Event(evnt || window.event);
      var eTarget = $(evnt.target);
      eTargetSel = this.getSelectorString(eTarget);
    }
    var errStack = this.getErrorStack(error);
    var errorUrl = '/JSErrorRecord';
    var debugKey = '';
    if (typeof(debugCacheKey) != "undefined") {
      debugKey = debugCacheKey;
    }
    var servlet = window.pageServlet;
    if(!servlet)
    {
      servlet = "unknown";
    }
    
    var eUrl = "none";
    try {
      eUrl = encodeURIComponent(window.location);
    }
    catch(e) {}
    var postData = {
      'eName': error.name, 
      'eMsg': error.message.replace(/\s/g, "_"), 
      'eBlk': errorBlock, 
      'eTarg': eTargetSel,
      'eStak': errStack,
      'xInfo': Json.toString(extraInfo),
      'key' : debugKey,
      'servlet': servlet,
      'eUrl': eUrl};
    new Ajax(errorUrl, {data: postData}).request();
  },
  
  getErrorStack: function(error) {
    var eStack = error.stack;
    if(eStack)
    {
      var stackArray = eStack.split("\n");
      stackArray = stackArray.map(function(pathElem) {
        var linePos = pathElem.lastIndexOf("/");
        if(linePos > 0)
        {
          pathElem = pathElem.substr(linePos+1);
        }
        return pathElem;
      });
      return stackArray.join();
    }
    else if(arguments && arguments.callee)
    {
      var callstack = [];
      var currentFunction = arguments.callee.caller;
      //skip the error record function
      currentFunction = currentFunction.caller;
      
      while (currentFunction) {
        var func = currentFunction.toString();
        var fnLen = func.length;
        var snipLen = fnLen > 75 ? 75 : fnLen;
        var fSnip = func.substring(0, snipLen);
        fSnip = fSnip.replace(/[\s\'\"]/g, "");
        fSnip += "::"; 
        callstack.push(fSnip);
        currentFunction = currentFunction.caller;
      }
      return callstack.join(" ");
    }
    return "none";
  },
  
  getSelectorString: function(elem) {
    if(!elem)
    {
      return "";
    }
   var xpath = [elem];
    while (typeof elem != "undefined" && elem && typeof elem.parentNode != "undefined")
    {
      xpath.push(elem = elem.parentNode);
    }
    xpath = xpath.map(function(elem) {
      if(elem && elem.tagName)
      {
        var elemSig = elem.tagName;
        if(elem.id)
        {
          elemSig += "#" + elem.id;
        }
        if(elem.className)
        {
          elemSig += "." + elem.className;
        }
        return elemSig;
      }
      return "";
    });
    xpath = xpath.reverse().join(">");
    xpath = xpath.replace(/\s+/g, ".");
    return xpath;
  }
};

/**
 * Utility methods for interacting with Facebook.
 * 
 * @class ta.support.Facebook
 */
ta.support.Facebook = {
  /**
   * Initialized Facebook JS.
   */
  setup: function(callback)
  {
    if (ta.has('facebook.included'))
    {
      callback();
      return;
    }

    // using xd_receiver from CDN, so document.domain must always be changed
//    window.documentDomainChanged = true;
//    document.domain = "tripadvisor.com";
//    ta.store('facebook.receiver', CDNHOST + '/facebook_xd_receiver_mod.htm');
    if (window.documentDomainChanged)
    {
      ta.store('facebook.receiver', '/facebook_xd_receiver_mod.htm');
    }
    else
    {
      ta.store('facebook.receiver', '/facebook_xd_receiver.htm');
    }


    var fn = callback;
    FB_RequireFeatures(["XFBML", "Api", "Connect"], function(){
      ta.store('facebook.included', true);
      FB.Facebook.init(ta.retrieve('facebook.apiKey'), ta.retrieve('facebook.receiver'),
        {
          doNotUseCachedConnectState: true,
          permsToRequestOnConnect: "email,friends_hometown,friends_location"
        }
      );
      fn();
    });
  },

  /**
   * Records that Facebook is available and sets up the page.
   */
  markAvailable: function()
  {
    ta.store('facebook.available', true);
    if (ta.has('facebook.onAvail'))
    {
      ta.remove('facebook.onAvail').each(function(fn){ fn(); });
    }
  },

  sessionAvailable: function()
  {
    ta.store('facebook.session.available', true);
    if (ta.has('facebook.onSessionAvail'))
    {
      ta.remove('facebook.onSessionAvail').each(function(fn){ fn(); });
    }
  },

  sessionUnavailable: function()
  {
    ta.store('facebook.session.available', false);
    if (ta.has('facebook.onSessionUnavail'))
    {
      ta.remove('facebook.onSessionUnavail').each(function(fn){ fn(); });
    }

    /* install tracking on "sign in" button (vfiano) */
    $$('#HEAD .FB_login_button').forEach(function (e) {
      e.addEvent('click', function () {
        new Ajax('/ActionRecord?action=FBC_Masthead').request();
    })});
    $$('#WoF_SigninLander .FB_login_button').forEach(function (e) {
      e.addEvent('click', function () {
        new Ajax('/ActionRecord?action=WoF_SigninLander').request();
    })});

  },

  checkSession: function()
  {
    FB.Connect.get_status().waitUntilReady(function(status) {
      var ourFBid = ta.retrieve('facebookuserid');
      var theirFBid = ta.support.Facebook.getUid();
      if(status == FB.ConnectState.connected && (!ta.retrieve('isUserLoggedIn') || (ourFBid == theirFBid))) { // session is only available if ids match
	ta.support.Facebook.sessionAvailable();
      } else {  // FB.ConnectState.appNotAuthorized or FB.ConnectState.userNotLoggedIn
        ta.support.Facebook.sessionUnavailable();
      }
    });
  },

  /**
   * Requests the user to login.
   *
   * @param pass function to call if user logs in
   * @param fail function to call if user aborts
   */
  requireSession: function(pass, fail)
  {
    FB.Connect.requireSession(pass, fail);
  },

  /**
   * Get the session object.
   */
  getSession: function()
  {
    return FB.Facebook.apiClient.get_session();
  },

  /**
   * Get the UID of the Facebook user.
   */
  getUid: function()
  {
    var session = ta.support.Facebook.getSession();
    return session ? session.uid : null;
  },

  /**
   * Get the Facebook session key.
   */
  getSessionKey: function()
  {
    var session = ta.support.Facebook.getSession();
    return session ? session.session_key : null;
  },

  /**
   * Checks if the user has connected their Facebook account with us. Runs the pass callback if so, the fail callback
   * otherwise.
   *
   * @param pass function to call if user is connected
   * @param fail function to call if user is not connected
   */
  ifUserConnected: function(pass, fail)
  {
    var p = pass;
    var f = fail;
    ta.support.Facebook.setup(function(){
      FB.ensureInit(function(){
        FB.Connect.ifUserConnected(p, f);
      });
    });
  },

  /**
   * Logs the user out of Facebook.
   *
   * @param callback function to call when user is logged out
   */
  logout: function(callback)
  {
    var cb = callback;
    FB.Connect.logout(function(result) {
      if (cb) cb(result);
    });
  },

  refreshSession: function(callback)
  {
    var cb = callback;
    FB.Connect.forceSessionRefresh(function(){
      if (cb) cb();
    });
  },

  logoutAndRefresh: function(callback)
  {
    var cb = callback;
    ta.support.Facebook.logout(function(result){
      ta.support.Facebook.refreshSession(cb);
    });
  },

  /**
   * Disconnect the user from the app.
   *
   * @param callback function to call after disconnect
   */
  revoke: function(callback)
  {
    var fn = callback;
    FB.Facebook.apiClient.revokeAuthorization(null, function(result, exception){
      if (fn) fn();
    });
  },

  /**
   * Disconnect the user from the app and log them out.
   *
   * @param callback function to call when user is logged out
   */
  revokeAndLogout: function(callback)
  {
    var fn = callback;
    ta.support.Facebook.revoke(function(){
      ta.support.Facebook.logoutAndRefresh(fn);
    });
  },

  onAvail: function(tunnel, id)
  {
    ta.fireEvent('facebook.onAvail');
  },

  switchUser: function()
  {
    new Asset.javascript('http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php',{
      onload: function(){
        ta.support.Facebook.setup(function(){
          FB.ensureInit(function(){
            ta.store('facebook.available', true);
            FB.Connect.logout(function(result){
              ta.overlays.registration.connectFB();
            });
          });
        });
      }
    });
  },

  onReady: function(frameId, button)
  {
    var frame = $(frameId);
    button = $(button);
    var size = button.getCoordinates();
    frame.setStyles({
      width: size.width,
      height: size.height
    });
    button.setStyle('visibility', 'visible');
  },

  onConnect: function()
  {
    ta.overlays.registration.getInstance({flow: ta.retrieve('facebook.flow')});
    ta.store('registration.action', "FB_CONNECT");
    ta.overlays.registration.log("FBC-start");
    ta.overlays.registration.post({action: 'FB_CONNECT'});
  },

  prepareIframe: function(frameId)
  {
    var contents = $(frameId + '-contents').childNodes[0].nodeValue;
    var frame = $(frameId);
    var doc = frame.contentDocument ? frame.contentDocument : frame.contentWindow.document;
    doc.body.innerHTML = '<html><head></head><body>' + contents + '</body></html>';
    doc.documentElement.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
    doc.documentElement.setAttribute('xmlns:fb', 'http://www.facebook.com/2008/fbml');

    var head = doc.getElementsByTagName('head')[0];

    var styleNode = doc.getElementsByTagName('style');
    if (styleNode.length > 0)
    {
      styleNode = styleNode[0];
      var styleText = styleNode.innerHTML;
      var styleElement = doc.createElement('style');
      styleElement.type = 'text/css';
      if (isIE) {
        styleElement.styleSheet.cssText = styleText;
      }
      else
      {
        styleElement.appendChild(doc.createTextNode(styleText));
      }
      styleNode.parentNode.removeChild(styleNode);
      head.appendChild(styleElement);
    }

    var scriptNode = doc.getElementsByTagName('script')[0];
    var scriptText = scriptNode.innerHTML;
    var scriptElement = doc.createElement('script');
    scriptElement.type = 'text/javascript';
    scriptElement.text = scriptText;
    scriptNode.parentNode.removeChild(scriptNode);
    head.appendChild(scriptElement);

    frame.contentWindow.loadFacebook();
  }
};
/**
 * Handles interaction with Facebook API.
 */
ta.support.FBConnector = new Class({
  initialize: function()
  {
    if (ta.retrieve('facebook.available'))
    {
      var pass = this.handleUserConnected.bind(this);
      var fail = this.requestLogin.bind(this);
      ta.store('registration.action', "FB_CONNECT");
      ta.overlays.registration.log("FBC-start");
      ta.support.Facebook.ifUserConnected(
        function() {
          pass();
        },
        function(){
          fail();
        }
      );
    }
    else
    {
      ta.overlays.registration.request('FB_UNAVAIL');
    }
  },

  /**
   * Callback if user is not logged in to Facebook.
   */
  requestLogin: function()
  {
    var pass = this.handleUserConnected.bind(this);
    var fail = this.userCancelled.bind(this);
    ta.util.pending.lock('facebook.login', function(){
      ta.support.Facebook.requireSession(pass, fail);
    });
  },

  /**
   * Callback if user connected with us
   */
  handleUserConnected: function()
  {
    ta.util.pending.lock('facebook.connect', function(){
      ta.overlays.registration.post({action: 'FB_CONNECT'});
    });
  },

  userCancelled: function()
  {
    ta.overlays.registration.cancel();
  },

  /**
   * Logs the user out of Facebook and re-initializes the flow.
   */
  restart: function(revoke, callback)
  {
    ta.store('registration.action', "FB_CONNECT");
    var fn = callback || this.requestLogin.bind(this);
    if (revoke) ta.support.Facebook.revokeAndLogout(fn);
    else ta.support.Facebook.logout(fn);
  }
});
ta.common.wisdomoffriends = {
  initiateLoggedOut: function() {
    loadFullCTA = false;

    $('WOFREST').addEvent('click', function () { 
	ta.common.wisdomoffriends.tracking('WoF_' + (wiFriHasMember ? 'CIV_' : '') + 'Signin' + (crPageServlet || "").replace('_', ''));
    });

    if (wiFriHasMember && modelLocaleCountry == 'US') {
      ta.common.wisdomoffriends.poll();
    } else {
      $("WoF-wrapper").removeClass('loading').show();
    }
    ta.common.wisdomoffriends.relocate();
  },
  initiate: function() {
    loadFullCTA = true;

    ta.common.wisdomoffriends.relocate();
    ta.common.wisdomoffriends.poll();
  },
  relocate: function() { }, // the position of the cta always depends on the value it provides, and on the tourism pages, the value of the boxes it displaces is significant, so the load position depends on the login status.
  afterLoad: [function () { ta.keep('wisdomoffriends.repliesBoxArgs', ta.common.wisdomoffriends.defaultRepliesBoxArgs() ); ta.common.wisdomoffriends.loadRepliesView(); }],
  afterLightboxed: function () { },

  guts: null,
  poll: function(s)
  {
    var guts = $('WoF-guts');
    var wrapper = $('WoF-wrapper');

    if(guts) {
      var isComplete;
      if (s) {
	if ( loadFullCTA ) {
          // payload is HTML
          guts.innerHTML = s;
	  if ($('WoF')) {
            isComplete = true;
          }
        } else {
          // payload is a simple value
          var count = ta.common.wisdomoffriends.inferredFriendCount = s.match(/<!-- friendsCount: ([0-9]+) -->/)[1];
          if (count != undefined) {
            isComplete = true;
          }
        }
      }
      else if (!ta.common.wisdomoffriends.loaded) {
        wrapper.addClass('loading');
        if (ta.remove('wof.showWelcome')) {
          new ta.overlays.PopoutWithOverlay({offset:{x:-26, y:20}, scrollOffset: -40, style:'s4 dg'}, wrapper).loadRemoteContent('/Registration?flow=WOF&action=FB_ADVICE&geo='+ta.retrieve('page.geo'));
        }
      }
 
      if(wrapper) {
      	wrapper.show();
      }	

      if(isComplete) {
        if (! ta.common.wisdomoffriends.loaded) {
          ta.common.wisdomoffriends.loaded = true;
          wrapper.removeClass('loading');

          if (loadFullCTA) {
            $('WoF-wrapper').show();
            ta.common.wisdomoffriends.afterLoad.each(function (f) { f() });
            ta.common.wisdomoffriends.requestLightbox();
            ta.common.wisdomoffriends.afterLightboxed();
          } else {
            if (ta.common.wisdomoffriends.inferredFriendCount > 0) {
              $('WoF-inferred-howknow').show();
              var content = $('WoF-inferred-CTA-content');
              content.innerHTML = content.innerHTML.replace(/%@/, ta.common.wisdomoffriends.inferredFriendCount);
              $('WoF-inferred-CTA-content').show();
              $('WoF-normal-CTA-content').hide();
              ta.fireEvent('wisdomoffriends.inferredCTALoad');
            }
          }

        }
      } else {
        ta.common.wisdomoffriends.requestUpdate.delay(250);
      }
    }
  },

  requestUpdate: function() {
	var pageProperty = 'WoF_FBC' + pageServlet;
	if(typeof showReviewSubtype != 'undefined' && showReviewSubtype){
	    pageProperty += showReviewSubtype;
	}
	
        var mtype = loadFullCTA ? 'explicit' : 'inferred';
        new Ajax('/WisdomOfFriendsController', {
            method: 'get',
            onComplete: function(s) { ta.common.wisdomoffriends.poll(s) },
            data: {'mtype': mtype, 'geo': modelGeoId, 'pageProperty' : pageProperty }
        }).request();
  },



  classToTrackingString: {
    'tab-askhow': 'WoF_WallPostOrPrivateMessage',
    'tab-wall': 'WoF_WallPost',
    'tab-msgs': 'WoF_PrivateMessage',
    'tab-newsfeed-extperm': 'WoF_ExtendedPermissions',
    'tab-newsfeed-extperm-yes': 'WoF_ExtendedPermGranted',
    'tab-newsfeed-extperm-no': 'WoF_ExtendedPermissionsNotGranted',
    'tab-wallpost-cancel': 'WoF_WallPostCancel',
    'tab-pvtmsg-email-done': 'WoF_PrivateMessageEmailSent',
    'tab-pvtmsg-pm-done': 'WoF_PrivateMessageFBSent'
  },

  newTab: function(className) {
    ta.common.wisdomoffriends.tracking(ta.common.wisdomoffriends.classToTrackingString[className]);
    ta.common.wisdomoffriends.clearCheckboxes();
    var oldClassName = $('WoF-lightboxable').className.replace(/^WoF-/,'');
    var newClassName = className;
    $('WoF-pvt-msg-textarea-default').value = (oldClassName=='tab-msgs' && newClassName.match(/^tab-pvtmsg-(pm|email)-done$/)) || (newClassName=='tab-msgs' && oldClassName.match(/^tab-pvtmsg-(pm|email)-done$/)) ? $('WoF-pvt-msg-textarea').value : '';
    $('WoF-lightboxable').className = 'WoF-' + className;
    if($('WoF-pvt-msg-faux-select').hasClass('visibility-hidden')) { ta.common.wisdomoffriends.pmDropdown() }
    ta.common.wisdomoffriends.pmOnclick('/img2/facebook/spacer.gif','',$('WoF-pvt-msg-name-default').value, '0', '/img2/facebook/spacer.gif', false);
    $('WoF-pvt-msg-how-sent').innerHTML = '';
    ta.common.wisdomoffriends.disablePMPreview();
    ['wall-post-textarea','pvt-msg-subject', 'pvt-msg-textarea'].forEach(function(id) { $('WoF-'+id).value = $('WoF-'+id+'-default').value; });
    ta.common.wisdomoffriends.lightboxable.position();
  },

  clearCheckboxes: function()
  {
    ['stay','eat','do'].forEach(function(checkboxSuffix){
      $('WoF-checkbox-'+checkboxSuffix).checked = false;
    });
    // $('WoF-CTA-ask-a-question').removeClass('WoF-button-disabled');
  },
  askForWallpostPermissions: function() {
    ta.common.wisdomoffriends.withWallPostPermission(
      function(){ ta.common.wisdomoffriends.newTab('tab-newsfeed-extperm-yes')},
      function(){ ta.common.wisdomoffriends.newTab('tab-newsfeed-extperm-no')}
    );
  },
  askForMessagePermissions: function() {
    alert("YOU SHOULD NOT ASK FOR MESSAGE PERMISSIONS"); return;
    ta.common.wisdomoffriends.withPrivateMessagePermission(
      function(){ ta.common.wisdomoffriends.newTab('tab-pvtmsg-extperm-yes')},
      function(){ ta.common.wisdomoffriends.newTab('tab-pvtmsg-extperm-no')}
    );
  },

  postWin: function(location_id, post_id, post_text, fb_id) {
    ta.common.wisdomoffriends.haveWallPostPermission(
      function(){ ta.common.wisdomoffriends.newTab('tab-newsfeed-extperm-yes')},
      function(){ ta.common.wisdomoffriends.newTab('tab-newsfeed-extperm')}
    );
    new Ajax('/WisdomOfFriendsAjax',
             {'method': 'post',
              'data': {'a': 'storeWallPostId',
                       'fb': fb_id,
                       'location_id': location_id,
                       'user_message': post_text.user_message,
                       'post_id': post_id
                      }
             }
            ).request();
    ta.keep('wisdomoffriends.refresh', true);
  },
  postFail: function() {
    ta.common.wisdomoffriends.newTab('tab-wallpost-cancel')
  },

  pmOnclick: function(imgsrc,howKnows,name,fbUid,fbAvatar,hasEmail) {
    if($('WoF-pvt-msg-textarea').value!='') { ta.common.wisdomoffriends.enablePMPreview(); }
    $('WoF-pvtmsg-recipient-skittle').src=imgsrc;
    $('WoF-pvtmsg-recipient-howKnows').innerHTML=howKnows;
    $('WoF-pvt-msg-img').src=fbAvatar;
    $('WoF-pvt-msg-name').innerHTML=name;
    $('WoF-pvt-msg-fbid').value=fbUid;
    $('WoF-pvtmsg-toggle').onmousedown();
    
    $('WoF-pvtmsg-preview-message-button').innerHTML = (hasEmail ? wof_pm_send : wof_pm_preview); // defined in header_resources

    $('WoF-pvt-msg-how-sent').innerHTML = (hasEmail ? wof_pm_your_message_will_be_sent_to_someone_by_email : wof_pm_your_message_will_be_sent_to_someone_on_facebook).replace(/12345/, name.match(/^[^ ]*/)[0]); // the reason we shove '12345' into the name slot is because Polish needs to decline the name as a foreign word

  },

  pmDropdown: function() {
    $$(".WoF-toggle-for-pm").forEach(function(e) { e.toggleClass('visibility-hidden') });
  },

  fbRead: function(readWhat, win, fail) {
    FB.Connect.showPermissionDialog('read_'+readWhat,
                                    function(authPerm) { (authPerm && authPerm.match(/read/)) ? win() : fail() });
  },
  withWallPostPermission: function(win, fail) {
    ta.common.wisdomoffriends.haveWallPostPermission(win,ta.common.wisdomoffriends.fbRead('stream',win,fail));
  },

  lightboxable: null,

  storeIntoLightboxable: function(s) {
    ta.common.wisdomoffriends.lightboxable = new ta.overlays.CenteredOverlay({
                                               "content": "<div id='WoF-lightbox-container'>" + s + "</div>",
                                               "style": "fb",
                                               "autoShow": false,
                                               "backdrop": ta.overlays.BACKDROP_ALWAYS});

    ta.common.wisdomoffriends.lightboxable.addEvent('onHide', 
      function() {    
	var className = ta.common.wisdomoffriends.lightboxable.inner.getElement('#WoF-lightboxable').className;
	ta.common.wisdomoffriends.tracking((ta.common.wisdomoffriends.classToTrackingString[className.replace(/^WoF-/,'')]) + '_Abort');
      });
  },

  requestLightbox: function(maybecallback) {
    if(!ta.common.wisdomoffriends.lightboxable) {
      ta.common.wisdomoffriends.lightboxable = 'loading';
      var pageProperty = 'WoF_FBC' + pageServlet;

      if(typeof showReviewSubtype != 'undefined'  && showReviewSubtype){
        pageProperty += showReviewSubtype;
      }
      new Ajax('/WisdomOfFriendsController',{ method: 'get', onComplete: function(s){ta.common.wisdomoffriends.storeIntoLightboxable(s); if(maybecallback) { maybecallback() }}, 
					      data: {'lightbox': 'true', 'geo': modelGeoId, 
					             'pageProperty' : pageProperty }
					    }).request();
    }
  },

  openLightbox: function(lightboxableClassName) {
    ta.common.wisdomoffriends.lightboxable.show();
    ta.common.wisdomoffriends.lightboxable.addEvent('onHide',
                function(programatic) {
                  if(!programatic && ta.retrieve('wisdomoffriends.refresh') != null && ta.retrieve('wisdomoffriends.refresh').length > 0 ) {
                    ta.remove('wisdomoffriends.repliesBoxArgs');
                    ta.keep('wisdomoffriends.repliesBoxArgs', ta.common.wisdomoffriends.defaultRepliesBoxArgs());
                    ta.common.wisdomoffriends.loadRepliesView();
                  };
                  ta.remove('wisdomoffriends.wallPreviewEnabled');
                  ta.remove('wisdomoffriends.pmPreviewEnabled');
                });
    ta.common.wisdomoffriends.newTab(lightboxableClassName || "tab-askhow");
  },
  openLightboxToWall: function() {
    ta.common.wisdomoffriends.openLightbox('tab-wall');
  },
  openLightboxToMessage: function(id) {
    ta.common.wisdomoffriends.openLightbox('tab-msgs');
    if(id) {
      ta.common.wisdomoffriends.pmDropdown();
      $('WoF-private-message-to-' + id).onclick();
    }
  },
  // Added to handle static Ask links that appear before WoF is fully loaded
  openLightboxWhenLightboxReady: function ( nMaxTries, bToWall ) {
    if( typeof(ta.common.wisdomoffriends.lightboxable) != "undefined" &&  ta.common.wisdomoffriends.lightboxable &&  ta.common.wisdomoffriends.lightboxable.show ) {
      if( bToWall ) {
        ta.common.wisdomoffriends.openLightboxToWall();
      } else {  // prompt for message type
        ta.common.wisdomoffriends.openLightbox();
      }
      ta.remove('fbLightboxTries');
    } else {
      var retries;
      if( ta.has('fbLightboxTries')) {
        retries = (ta.retrieve('fbLightboxTries') + 1);
      } else {
        retries = 1;
      }
      if( retries < nMaxTries ) {            
        ta.store('fbLightboxTries', retries );
        setTimeout( function() { ta.common.wisdomoffriends.openLightboxWhenLightboxReady(nMaxTries, bToWall ) }, 1500 ); 
      } else {
        ta.remove('fbLightboxTries');
        location.replace('/TripFriends');  // send to WoF lander if lightbox is unresponsive.
      }
    }  // lightboxable
  },
  
  // Ask cta on logos etc.
  askFromStaticContext: function ( bToWall, nPID, sTrackingLabel, nTries ) {
    if( nPID) { 
      setPID(nPID);
    }
    if( sTrackingLabel ) {
      ta.common.wisdomoffriends.trackingByServlet(sTrackingLabel);
    }
    ta.common.wisdomoffriends.openLightboxWhenLightboxReady(nTries, bToWall);
  },  
  
  tabstep: function(tab) {
    $("WoF").className = "WoF-tab-" + tab
  },

  headshotMouseover: function(id)
  {
    ta.util.pending.cancel('wof-headshot-hide');
    ta.selectInvoke('.WoF-friends-teaser-text span','hide');
    var elem = $(id);
    elem.style.display='';
    elem.style.visibility='hidden';
    setTimeout(function(){elem.style.visibility='visible'},50);
  },

  headshotMouseout: function()
  {
    ta.util.pending.wait('wof-headshot-hide', function(){
      ta.selectInvoke('.WoF-friends-teaser-text span','hide');
      $$('.WoF-friends-teaser-text span.default')[0].style.display='';
    }, 100);
  },
   
  /*
    args.page
    args.pageSize
    args.tstamp
    args.noWall
    args.noPM
  */ 
   
  fetchReplies: function(event, elem, args) {
    var page = args.page;
    var pageSize = args.pageSize;
    var tstamp = args.tstamp;
    var noPM = args.noPM;
    var noWall = args.noWall;
    var geo = args.geo;
    var mini = args.mini;   // box vs member profile
    
    var url = '/WisdomOfFriendsAjax?a=getReplies&p=' + page + '&pageSize=' + pageSize;
    
    if(tstamp != null)
    {
        url += ('&tstamp=' + tstamp);
    }
    if(noPM)
    {
        url += ('&noPM=true');
    }
    if(noWall)
    {
        url += ('&noWall=true');
    }
    if(geo)
    {
        url += ('&geo=' + geo);
    }
    if(mini)
    {
        url += ('&mini=true');
    }
   
    new Ajax(url, {
        method: 'get',
        onComplete: function(resp) { 
            setTimeout(function() {
                elem.innerHTML = resp;
            
                var replyCountDiv = $('WOF_REPLY_COUNT_VAL'); 
                var replyCountSpan = $('WOF_REPLY_COUNT'); 
                if(replyCountDiv && replyCountSpan) { 
		    ta.store('wisdomoffriends.reply_count', replyCountDiv.innerHTML * 1);
                    replyCountSpan.innerHTML = replyCountDiv.innerHTML;
                    replyCountSpan.show(); 
                }
            }, 0);
        }
    }).request();  
   },

  havePermission: function(permission, success, fail)
  {
    var fnSuccess = success;
    var fnFail = fail;
    FB.Facebook.apiClient.users_hasAppPermission(permission, function(result){
      if (result != 0) {
        if(fnSuccess) {
            fnSuccess();
        }
      }
      else if (fnFail) fnFail();
    });
  },

  haveWallPostPermission: function(success, fail)
  {
    ta.common.wisdomoffriends.havePermission('read_stream', success, fail);
  },

  expandReplies: function(event, elem, arg)
  {
    arg.getElements('div.WoF-expand').each(function(e) { e.hide(); } );
  
    arg.getElements('table.WoF-reply-content').each(function(e) {
        e.style.display = '';
    });
  },

  showWelcome: function()
  {
    ta.store('wof.showWelcome', true);
  },

  /*
    args.page
    args.pageSize
    args.tstamp
  */

  loadRepliesView: function()
  {    
    var args = ta.retrieve('wisdomoffriends.repliesBoxArgs')[0];
    args.noWall = false;
    args.noPM = false;
    args.geo = null;
    
    if(!ta.has('wisdomoffriends.load_count')) {
        ta.store('wisdomoffriends.load_count', 1);
    } 
    
    var count = ta.retrieve('wisdomoffriends.load_count') + 0;
    
    var finish = function(c) {
        var curCount = ta.retrieve('wisdomoffriends.load_count');
        if(!ta.has('wisdomoffriends.load_count') || c == curCount) {
            ta.common.wisdomoffriends.fetchReplies(null, $('WOF_REPLIES_BOX'), ta.retrieve('wisdomoffriends.repliesBoxArgs')[0]);
            
            ta.store('wisdomoffriends.load_count', ta.retrieve('wisdomoffriends.load_count') + 1);
        }
    };

    var failWallFn = function() {
        ta.keep('wisdomoffriends.askFor', [ta.common.wisdomoffriends.withWallPostPermission]);
        ta.retrieve('wisdomoffriends.repliesBoxArgs')[0].noWall = true;
    };

    var checkWall = function() {
        // timeout in case FB never gets back to us
        setTimeout(function(){ finish(count); ta.store('wisdomoffriends.fbtimeout', true); }, 3000);
        ta.common.wisdomoffriends.haveWallPostPermission(function() { finish(count); } , function() { failWallFn(); finish(count); } );
    };

    checkWall();
  },

  askForNeededPermissions: function()
  {
    if(ta.has('wisdomoffriends.askFor')) {

        var call = function(i) {

            if(ta.retrieve('wisdomoffriends.askFor')[i]) {
                ta.retrieve('wisdomoffriends.askFor')[i]( function() { call(i+1); }, function() { } );
            } else {
                window.location = window.location;
            }
        };

        call(0);
    }
  },
  
  markRead: function(event, elem)
  {
    if(elem) { elem.hide(); }
    new Ajax('/WisdomOfFriendsAjax?a=markRead', { method:'get' }).request();  
  },

  enableWallPreview: function(cb)
  {
    var bShouldEnable = ($('WoF-wall-post-textarea').value.length > 0) || ($('WoF-checkbox-stay').checked) || ($('WoF-checkbox-eat').checked) || ($('WoF-checkbox-do').checked)
    if (bShouldEnable) {
      if(!ta.retrieve('wisdomoffriends.wallPreviewEnabled'))
      {     
          $('WoF-CTA-ask-a-question').removeClass('WoF-button-disabled');
          ta.keep('wisdomoffriends.wallPreviewEnabled', true);
      }     
    }
    else {
      if(ta.retrieve('wisdomoffriends.wallPreviewEnabled'))
      {     
          $('WoF-CTA-ask-a-question').addClass('WoF-button-disabled');
          ta.remove('wisdomoffriends.wallPreviewEnabled');
      }     
    }
  },
  
  enablePMPreview: function(cb)
  {
    if(!ta.retrieve('wisdomoffriends.pmPreviewEnabled'))
    {
        var button = $('WoF-pvtmsg-preview-message-button'); 
        button.removeClass('WoF-button-disabled');
        ta.keep('wisdomoffriends.pmPreviewEnabled', true);
    }
  },

  disablePMPreview: function() {
    if(ta.retrieve('wisdomoffriends.pmPreviewEnabled')) {
      $('WoF-pvtmsg-preview-message-button').addClass('WoF-button-disabled');
      ta.keep('wisdsomoffriends.pmPreviewEnabled', false);
    }
  },
  
  defaultRepliesBoxArgs: function() {
    return { pageSize: 2, page: 0, geo: modelGeoId, mini: true };
  },
  
  testButtonDisability: function(button, textarea, maybeNonZeroValue) {
    var b = $(button), t = $(textarea), nz = $(maybeNonZeroValue);
    if(t.value.length > 1400) { t.value = t.value.substring(0,1400) }
    var implies = function(a,b) { return !a || eval(b) };  // eval() because _if_ evaluates its parameters lazily
    b[t.value.length > 0 && implies(nz, "nz.value != 0") ? "removeClass" : "addClass"]("WoF-button-disabled")
  },

  sendWallPost: function(button) {
    if(button.hasClass('WoF-button-disabled'))
      return;
    ta.common.wisdomoffriends.lightboxable.container.style.left='-9999px'; // because we recenter at every tab-change.
    var wofwpt=$('WoF-wall-post-textarea').value;
    var questions=$$('#WoF-checkboxes input').filter(function(i){return i.checked}).map(function(input,index){return $(input.id + (index==0 ? '-first' : '-second')).innerHTML }).join(' ');
    var fullQuestionText = wofwpt=='' ? questions : wofwpt;
    var wiFriGeoName = $('wof_wiFriGeoName').innerHTML;
    var wof_buildTourismUrl = $('wof_buildTourismUrl').innerHTML;
    var href = 'http://' + window.location.hostname + wof_buildTourismUrl;
    var wiFriGeoImageUrl = $('wof_wiFriGeoImageUrl').innerHTML;
    var wiFriCivUrl = $('wof_wiFriCivUrl').innerHTML;
    var fbuid = $('wof_fbuid').innerHTML;

    FB.Connect.streamPublish(
      fullQuestionText, {'caption': wof_streampublish_caption.replace(/12345/,wiFriGeoName),
                         'href': href,
                         'name': wof_lightbox_somewhere_on_tripadvisor.replace(/12345/,wiFriGeoName),
                         'media': [{'type': 'image', 'src': wiFriGeoImageUrl, 'href': href}]},
                        [{'text': wof_lightbox_map_your_travels, 'href': wiFriCivUrl}],
                        null,
                        wof_lightbox_whats_on_your_mind,
                        function(post_id,error,post_text) { ta.common.wisdomoffriends[(post_id+'')!='null' ? 'postWin' : 'postFail'](ta.retrieve('page.location'),post_id,post_text,fbuid) });
    ta.common.wisdomoffriends.clearCheckboxes();
  },

  sendPM: function(disablableButton) {
    if(disablableButton.hasClass('WoF-button-disabled')) {
        return;
    }
        
    $('WoF-pvtmsg-error').hide();
    
    $$('.WoF-pm-confirmation-name').forEach(function(e) { e.innerHTML = $('WoF-pvt-msg-name').innerHTML });
    
    new Ajax('/WisdomOfFriendsAjax', {
        data: {
            'a': 'storePrivateMessage', 
            'location_id': ta.retrieve('page.location'), 
            'recipient': $('WoF-pvt-msg-fbid').value, 
            'subject': $('WoF-pvt-msg-subject').value, 
            'body': $('WoF-pvt-msg-textarea').value
        },
        onSuccess: function(resp) {
            // should we send a facebook pm? - returns
            var m = resp.match(/<!-- pm ([A-Za-z0-9_-]{32}) -->/);
            if(m && m.length == 2) {
                var lnk = 'http://' + window.location.host + '/ShowConversation?id=' + m[1];
                
                $('WoF-pvt-msg-message').value = $('WoF-pvt-msg-textarea').value + '\n\n' + "Reply on TripAdvisor: " + lnk + " \n";
                $('WoF-pm-form').submit();
                ta.common.wisdomoffriends.newTab('tab-pvtmsg-pm-done');
                ta.keep('wisdomoffriends.refresh', true);
                return;
            } 
            
            // did we send an email? - returns
            m = resp.match(/<!-- email ([A-Za-z0-9_-]{32}) -->/);
            if(m && m.length == 2) {
                ta.common.wisdomoffriends.newTab('tab-pvtmsg-email-done');
                return;
            }
            
            // error :-(
            $('WoF-pvtmsg-error').show();
        }, 
        onFailure: function(resp) { $('WoF-pvtmsg-error').show(); },
        method: 'post'
    }).request();
  },

  trackingByServlet: function(clickTarget) {
    var pageString = 'WoF_' + pageServlet + '_';

    if(typeof showReviewSubtype != 'undefined' && showReviewSubtype) {
        pageString += (showReviewSubtype + "_");
    }
    
    ta.common.wisdomoffriends.tracking(pageString + clickTarget);
  },

  trackingReplyTab: function() {
    var hasReplies = ta.has('wisdomoffriends.reply_count') && (ta.retrieve('wisdomoffriends.reply_count') > 0);
    ta.common.wisdomoffriends.tracking(hasReplies ? 'WoF_RepliesTabNewReplies' : 'WoF_RepliesTabNoReplies');
  },

  tracking: function(actionName) {
    new Ajax('/ActionRecord?action=' + actionName).request();
  },

  forgetCIV: function() {
    Cookie.remove('TASocialNetwork', {domain: '.tripadvisor.com', path: "/"});
  },
  
   // get 3 random profile pix
  getFilmstrip: function ( tgtElemId ) {
    var tgtElem = $(tgtElemId);
    if(tgtElem) { 
      new Ajax('/WisdomOfFriendsAjax?a=getFilmstrip', {
        method: 'get',
        onComplete: function(txt) {
          tgtElem.innerHTML = txt;   
        }
      }).request();
    }
  }
};
/**
 * 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: 300,
		observerOptions: {},
		fxOptions: {},
		overflown: [],
		selectOnBlur: false,
		delayHide: 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.ie || window.webkit ? '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 exists so that pages may conditionally hide the choices (see: neFlights.js)
		if(!this.options.delayHide) {
			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;
    },

    readSession: function(key, options) {
        if (!$defined(options)) options = {};
        var cookie = Cookie.getRaw('TASession');
        var valPattern = options.pattern ? options.pattern : '[^\\*]+';
        var pattern = new RegExp('(\\*|^)' + key + '\\.(' + valPattern + ')');
        if (pattern.test(cookie)) {
            return {cookie: cookie, pattern: pattern, start: RegExp.$1, value: RegExp.$2};
        }
        return {cookie: cookie, pattern: pattern};
    },

    writeSession: function(key, value, options) {
        var sv = this.readSession(key, options);
        if (!sv.cookie) return false;

        var newVal;
        if ($defined(sv.value)) {
            newVal = sv.cookie.replace(sv.pattern, sv.start + key + '.' + value);
        } else {
            newVal = sv.cookie + '*' + key + '.' + value;
        }
        Cookie.setRaw('TASession', newVal, {domain: cookieDomain});
        return true;
    }
});

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 subdomains
  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 for SEM autocompletion on search bar
Autocompleter.Ajax.JsonSEM = Autocompleter.Ajax.Json2.extend(
{
  options: {
    className: 'semauto-choices',
    minLength: 3,       
    useSelection: false,
    maxChoices: 15,
    choicesToShow: 10
  },

	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;
			default: $('mainSearchSubmit').onclick = function () { return true; };
		}
		this.value = false;
	},

	showChoicesWidthFix: function(width) {
		if (this.visible || !this.choices.getFirst()) return;
		this.visible = true;
		var pos = this.element.getCoordinates(this.options.overflown);
		this.choices.setStyles({
			left: pos.left - 8,
			top: pos.bottom
		});
                // total hack for widths on ie6 and ie7
		if (window.ie6 || window.ie7) this.choices.setStyle('width', width);
		this.fx.start(1);
                //comment these out so nothing is selected
	        this.choiceOver(this.choices.getFirst());
		this.fireEvent('onShow', [this.element, this.choices]);
	},

	/**
	 * markQueryValue
	 *
         * different behavior: uses each word in the input string instead of just the whole thing
	 * 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) {
            if (!(this.options.markQuery && this.queryValue)) return "";

            var querywords = this.queryValue.split(" ");
            var bigregexp = "";
            querywords.each(function(w) {
                bigregexp = bigregexp + w.escapeRegExp() + '|';
            });
            bigregexp = bigregexp.substring(0,bigregexp.length-1);
            var answer = txt.replace(new RegExp('(' + bigregexp + ')', 'gi'), '<span class="autocompleter-queried">$1</span>');
            return answer;
	},


  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;
    var header;
    var hotelcount = 0, geocount = 0;
    var maxlength = 0;

    header = new Element('span').setHTML('');
    header.injectInside(this.choices);

    choices.each(this.options.injectChoice || function(choice, i)
    {
      var el;
      if (choice.type=='geo' && geocount < this.options.choicesToShow ) {
        geocount++;
        if (geocount==1) {
            el = new Element('li').setHTML('<div style="height: 100%; overflow: hidden;"><div class="hideonchoice" style="float: right; color: #589442; padding-right: 6px; padding-left: 100px; ">Popular Destinations <img style="padding-top: 2px;" src="img2/icons/typeahead/d.gif"/></div><div style="float: left; padding-left: 6px;">'+this.markQueryValue(choice.name)+'</div></div>');
        }
        else {
            el = new Element('li').setHTML('<span style="padding-left: 6px; padding-right: 6px;">'+this.markQueryValue(choice.name)+'</span>');
        }
        el.responseObj = choice;
        el.inputValue = choice.name;
        maxlength = Math.max(maxlength, choice.name.length);
        this.addChoiceEvents(el).injectInside(this.choices);
      }
    }, this);

    choices.each(this.options.injectChoice || function(choice, i){
      if (choice.type=='hotel' && geocount+hotelcount < this.options.choicesToShow ) {
        hotelcount++;
        var el;
        if (hotelcount==1) {
            el = new Element('li').setHTML('<div style="padding-top: 0px; margin-top: 0px; border-top: 1px solid #d3e0bb; height: 100%; overflow: hidden;"><div class="hideonchoice" style="float: right; color: #589442; padding-right: 6px; padding-left: 100px; ">Popular Hotels <img style="padding-top: 2px;" src="img2/icons/typeahead/h.gif"/></div><div style="float: left; padding-left: 6px;">'+this.markQueryValue(choice.name)+'</div></div>');
        }
        else {
          el = new Element('li').setHTML('<span style="padding-left: 6px; padding-right: 6px;">'+this.markQueryValue(choice.name)+'</span>');
        }
        el.responseObj = choice;
        el.inputValue = choice.name;
        maxlength = Math.max(maxlength, choice.name.length);
        this.addChoiceEvents(el).injectInside(this.choices);
      }
    }, this);
    var widthForIE = 0;
    if (hotelcount+geocount==0)
    {
      header = new Element('li').setHTML('<div style="border-top: 1px solid #d3e0bb; color: #1f2c92; padding-left: 10px; padding-right: 8px; font-family:Arial, Helvetica, sans-serif; ">Find more results for "'+this.queryValue+'"<span style="font-size: 75%;"> >></div>');
      maxlength = this.queryValue.length+25;
      widthForIE = maxlength*8;
    }
    else
    {
        header = new Element('li').setHTML('<div style="border-top: 1px solid #d3e0bb; text-align: right; color: #1f2c92; padding-right: 6px; font-family:Arial, Helvetica, sans-serif; ">Find more results for "'+this.queryValue+'"<span style="font-size: 75%;"> >></span></div>');
      widthForIE = maxlength*10+110;
    }


    header.setAttribute('class','moreresults');
    header.responseObj = { 'title': 'extralink', 'name': 'foo', 'value': 0, 'type': 'other', 'url': 'Search?q='+this.queryValue };
    header.inputValue = this.queryValue;
    this.addChoiceEvents(header).injectInside(this.choices);
    this.showChoicesWidthFix(widthForIE);
  }
});

// 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++){
      try{
        this.rules[rule](elements[y]);
      }
      catch(e){
        ta.util.error.record(e, 'rules', null, {'element': ta.util.error.getSelectorString(elements[y]), 'ruleCount': 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();

ta.queueForLoad(function() {
  Cookie.remove('NPID');

  //internet explorer fires onload event for every iframe, we only want top window
  if(window == window.top && !window.opener) {  
    var c = Cookie.get('ajaxAction');
    if (c) {
      Cookie.remove('ajaxAction', {domain:cookieDomain, path:"/"});
      c = c.split('|');
      // 0 - function to call, 1..N - parameters as a strings
      // try to find and run the function
      try {
        var fn = eval(c[0]);
        if ($defined(fn) && $type(fn) == "function") fn.attempt(c.slice(1));
      }
      catch (_ex) {
        ta.util.error.record(_ex, 'ajaxAction', evnt, {'funcName': c[0]});
      }
    }
  }

  // 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.
if (window.ie && (typeof semHotelBoxFocus != "undefined") && semHotelBoxFocus)
{  
  ta.queueForLoad(behaviorFunction);
}
else 
{
  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
  if (!window.ie6)
  {
    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].hasClass('active')) {
        showIndex = i;
        break;
      }
      /*      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 (delay 200ms after load)
  freshVSCookieCheck.delay(200);
}

function freshVSCookieCheck()
{
  // 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()
{
  var searchbox = $('mainSearch');
  if(searchbox && searchbox.className.match(/\b(focusOnLoad)\b/))
  {
    try {
      searchbox.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_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) { 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]); };

// 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;
}

rules['#NEWSLETTER_CALLOUT input.memberUpdateEmail[type=text]'] = function(elmt) {
  $('memberUpdatesEmailDefault').hide();
  $(elmt.form).addEvent('submit', function(e) {
  	if(elmt.value == $('memberUpdatesEmailDefault').innerHTML)
  	{
  		elmt.value = ""; 		
  	}
  });
}

function alertIfEmpty(elmt) 
{
  if (elmt && 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 || window.event).stop();
    return false;
  }
  return true;
}

// collapsible content - hide initially
var toggleMeRule = function(elmt) {
  if( false == elmt.hasClass('defaultOpen') ) {
    elmt.addClass('off');
  }

  elmt.getElements('.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';
    }
  });
}

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);
  }
}

function getRelativeURL() {
  return window.location.pathname + window.location.search + window.location.hash;
}

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['.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";

// 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) } );
    }
  } 
 }

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 = "&";
  if (popupConfig.servletUrl.contains("fromServlet="))
  {
    var url = popupConfig.servletUrl+sep+"Action="+action;
  }
  else
  {
    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 (typeof isOverlayServlet != 'undefined' && isOverlayServlet) // bug 42382
  { 
    // See DhtmlPopup.getPopupOptions
    if( !overlayOptions || overlayOptions == "false" )
    {
      var opts = {};
    }
	else
	{
	  var opts = overlayOptions;
	}
    opts.permanent = true;
    showDHTMLPopup(isOverlayServlet, ta.util.ASDF.asdfDcd(overlayServletUrl), overlayLightbox, opts);
  }

  if (typeof IS_OVERLAY_DEBUG != 'undefined' && 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);
      
      if (popupConfig.servlet == 'RuleBasedPopup' && popupConfig.options.cmsDisplayName)
      {
        overlay.container.addClass(popupConfig.options.cmsDisplayName);
      }
      
      // 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(); 
    // Need to use inner{Width,Height} on the iPhone to handle zooming in on the popup properly
    // Blackberry doesn't have scroll{Left,Top}
    this.setStyles({
      left: ((window.innerWidth || window.getWidth()) - cs.size.x) / 2 + (window.getScrollLeft()||0),
      top: ((window.innerHeight || window.getHeight()) - cs.size.y) / 2 + (window.getScrollTop()||0)
    });
  }
  // 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');
  }
}

var cpu_win;
function verifyPopunderExists(delay)
{
	try 
	{
		if (cpu_win && cpu_win.location)
		{
			return;
		}
	}
	catch (error) { }

	new Ajax("/ActionRecord?action=EXITWINDOWLOG_popunderfollowup|fail|delay=" + delay).request();
}

// Bug 27446
if (window.opener == null && typeof(cpu_enabled) != "undefined")
{   
  var cpu_paused = false;
  var cpu_go = function(e, triggerType) {
    try {
      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;               
        }

        if (/FBConnectButton/.test(eTarget.className))
        {
          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;
      }
          
      if(typeof pageServlet == "undefined")
      {
        pageServlet = "";
      } 

      //don't spawn MPU if coming from Flights servlet or VacationRentals servlet
      if((IS_MPU_ENABLED || IS_AWPU_ENABLED) && ("Flights" == pageServlet || "VacationRentals" == 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 || IS_AWPU_ENABLED || IS_DDPU_ENABLED || IS_LPU_ENABLED))
      {
        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 || IS_AWPU_ENABLED || IS_DDPU_ENABLED || IS_LPU_ENABLED)) // if no cookie or no commerce-valid loc data in cookie
      {
        return;
      }
      var w = 1;
      var h = 1;
      try{
        if (window.ie7) {w = 250; h = 100;}
        if (window.webkit) { w = 85; h = 100;}
      }
      catch(error){
        ta.util.error.record(error, 'commercePopunder - access to window attributes', e, {'type': triggerType});  
      }                                          

      var nX = 0, nY = 0;
      try{
        nX = (window.screenLeft || window.screenX);
        nY = (window.screenTop || window.screenY);
      }
      catch(e){
        ta.util.error.record(error, 'commercePopunder - setting nX and nY', e, {'type': triggerType});  
        nX = undefined;
        nY = undefined;
      }

      var x = 0, y = 0;
      try{
        if (typeof(nX) == "undefined") { // full screen
          nX = 0;
          nY = 0;
          x = nX + window.screen.availWidth - w; // bottom-right corner of window
          y = nY + window.screen.availHeight - h;
        }
        else{
          x = nX + window.getWidth() - w; // bottom-right corner of window
          y = nY + window.getHeight() - h;
        }
      }
      catch(error){
        ta.util.error.record(error, 'commercePopunder - setting x and y', e, {'type': triggerType});  
        x = 200;
        y = 200;
      }
      
      try {
        if(window.ie7)
        {
          y = y - 95;
          x = x + 13;
        }
        else if(window.webkit)
        { 
          y = y - 16;
        }
        else
        {
          x = 5000;
          y = 5000;
        }
      }
      catch(error) {
        ta.util.error.record(error, 'commercePopunder - access to window attributes', e, {'type': triggerType}); 
      }
      if (! ta.retrieve("exitwindow.set_cookie_after_popup"))
      {
	      var cookieDuration = ta.retrieve('exitwindow.cpu_cookie_duration') || 5;
	      if(IS_MPU_ENABLED || IS_AWPU_ENABLED)
	      {
	        cookieDuration = ta.retrieve('exitwindow.mpu_cookie_duration') || 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_AWPU_ENABLED && "CheapFlights" != pageServlet)
      {
          cpu_uri = cpu_uri + "&aw=1";
          width = 718;
          height = 600;
      }
      else if(IS_MPU_ENABLED && "CheapFlights" != pageServlet)
      {
        cpu_uri = cpu_uri + "&flights=1";
        width = 715;
        height = 450;
      } else if(IS_DDPU_ENABLED) {
        width = 600;
        height = 475;
      }
      else if(IS_LPU_ENABLED)
      {
          cpu_uri = cpu_uri + "&lufthansa=1";
          width = 639;
          height = 480;
      }
      
      if (triggerType == 'click')
      {
          cpu_uri = cpu_uri + "&onclick=1"
      }
          
      // record attempt to launch popunder
      try{
        var scrollBarsOption = "1";
        if(window.chrome)
        {
          scrollBarsOption = "0";
        }
        cpu_win = window.open(cpu_uri, "CommercePopunder", "toolbar=1,location=1,directories=1,status=1,menubar=1,resizable=1,copyhistory=1,scrollbars=" + scrollBarsOption + ",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);
  		  if (ta.retrieve("exitwindow.extended_tracking_feature_enabled"))
		  {
	        verificationDelay = ta.retrieve('exitwindow.popup_verification_delay') || 3000;
	        setTimeout("verifyPopunderExists(" + verificationDelay + ")", verificationDelay);
		  }
        }
        else
        {
          // If this was an unload CPU...
          if (triggerType == 'unload')
          {
            // Then suppress future unloads (allowing clicks)
            Cookie.set("CommercePopunder", "SuppressUnload", {domain: cookieDomain, duration: cookieDuration});
          }
        }
      }
      catch(error){
        ta.util.error.record(error, 'commercePopunder - pop window', e, {'type': triggerType});
      }
    }
    catch(err) {
      ta.util.error.record(err, 'commercePopunder', e, {'type': triggerType});
    }
    
  } // 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]));
}

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);
  }
}

function injectAdsTargeted(elmt, onload)
{
  var ads = elmt.getElements(".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));
    if ($defined(onload)) $(el).addEvent('load', onload);
    ads[i].getParent().appendChild(el);
  }
}

function showOwnersDHTMLPopup(popupServlet, popupServletUrl)
{
    var popupOps = {
        callback: enableAffilBox
    }
    showDHTMLPopup(popupServlet, popupServletUrl, true, popupOps);
}

// Workaround for IE6 where we hide all select boxes in DHTML
// popups - re-enable owners reg Affiliation select box
function enableAffilBox(popupDiv)
{
  if (typeof popupDiv == 'undefined' || popupDiv == null)
  {
    return;
  }
  for (var i=0; i < 3; i++)
  {
    var affilId = "affil_" + i;
    var elem = popupDiv.getElementById(affilId);
    if (elem != null && elem != false && elem != "")
    {
      elem.style.visibility="visible";
    }
  }
}

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 = ta.util.element.replaceContent;

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'] = ta.widgets.tabContainer;

//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();
  });
};

//  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) {
  if (ta && ta.queueForLoad) {
    ta.queueForLoad(fn);
  } else {
    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;
}

function showPrivacyPolicy() {
    window.open("/pages/privacy.html", "PrivacyPolicy", "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=3,width=800,height=600,screenX=30,screenY=25,left=30,top=25");
}
