bplist00_WebMainResource_WebSubresources_WebSubframeArchives ^WebResourceURL_WebResourceTextEncodingName_WebResourceMIMEType_WebResourceData_WebResourceFrameName_|http://www.tripadvisor.com/Hotel_Review-g186612-d1097217-Reviews-Mystical_Rose_Bed_and_Breakfast-Killarney_County_Kerry.htmlUUTF-8Ytext/htmlOM Mystical Rose Bed and Breakfast (Killarney, Ireland) - B&B Reviews - TripAdvisor
Free Newsletter

Interested in Mystical Rose Bed and Breakfast and Killarney?

We'll send you updates with the latest deals, reviews and articles for Mystical Rose Bed and Breakfast and Killarney each week.

  • Save Mystical Rose Bed and Breakfast
  • E-mail this page

Mystical Rose Bed and Breakfast


Woodlawn | Woodlawn, Killarney, Ireland

Reviews you can trust

88% Recommend
4.5 of 5 stars 9 reviews
Excellent
Very good
Average
0
Poor
0
Terrible
0
By trip type
All (9)
Business (0)
[ Friends first ] [ Date ] [ Rating ]
Save Review
4 of 5 stars
Broekhuizen   1 contribution
Hoorn (NH)
Nov 1, 2009 | Trip type: Couples

Lovely B&B with beautifull garden. Noreen is a friendly host who likes to entertain you with traditional Irish dance. She has traveled a lot around the world and likes to talk about it. Very good affordable restaurant nearby (on walking distance). Situated in quiet neigbourhood, close to city center and tourist attractions like Killarney National Park and Ring of Kerry.... more

Gorgeous B&B
Save Review
5 of 5 stars
MelMainz   4 contributions
Mainz
Oct 1, 2009 | Trip type: Solo travel

I was in Killarney, Mystical Rose, for five nights in September. Noreen, her B&B and the family were very gorgeous and nice. The Mystical Rose is some minutes walkway from the town of Killarney. The B&B is very nice and Noreen tried to read your whishes from your eyes ;-). I have gained Mystical Rose and Noreen very much, and... more

Save Review
4 of 5 stars
Momsterdeb   3 contributions
Longmeadow
Aug 25, 2009

If you're looking for a posh place to stay, this isn't it. But if you're looking to meet interesting people and learn more about the area you won't find a sweeter or more knowledgable host. A word of caution - Miss Noreen can be very exuberant and a bit quirky - it may throw you at first, but spend some... more

Save Review
5 of 5 stars
scoroy   1 contribution
Lynn Haven, Florida
Jul 22, 2009 | Trip type: Family

Our stay at the Mystical Rose was the most pleasant experience on our visit to Ireland. We stayed at several very nice places in Ireland, but none compare to our visit with Noreen and her wonderful family. Yes, we met her father and several neighbors of hers that was like family to her. We were originally to stay only one... more

Save Review
5 of 5 stars
emamaryjane   4 contributions
new york- usa
Apr 8, 2009 | Trip type: Friends getaway

We stayed at the Mystical Rose twice! the Mystical Rose is quaint and the gardens and bird activity a real delight, Mystical Rose is close to fine shopping and dining in the beautiful town of Killarney. The Killarney National Park is right there and the Lakes of Killarney also a short drive away. If you stay at the Mystical Rose... more

Save Review
5 of 5 stars
singinnurse53   3 contributions
new jersey
Feb 22, 2009 | Trip type: Couples

we have stayed at mystical rose twice -- in 2004 and 2006.... the first time, we arrived in Killarney with B and B guide in hand, and found our way to Mystical Rose... Noreen welcomed us and when i asked about taking in a jaunting car ride that evening, she arranged for one to pick us up at her door!... more

Save Review
5 of 5 stars
Irishgrl_7   34 contributions
Iowa
Jul 3, 2008 | Trip type: Family

Mystical Rose B&B is a lovely home minutes from the center of Killarney. Noreen O'Mahoney welcomes visitors into her childhood home- which has been added on to with purpose built rooms- with unmatched Irish hospitality. The rooms are on the smaller side but have everything you need for a comfortable stay- ensuite baths (with shower), 1 or 2 beds, side... more

amusant
Save Review
4 of 5 stars
mulder38   10 contributions
Grenoble
Sep 20, 2009 | Trip type: Family

Très bon accueil de Noreen qui est l'attraction du B&B!!! chambres propres et confortables B&B a 20 minutes a pied... more

Save Review
5 of 5 stars
MALUCarpentras   1 contribution
CARPENTRAS
Aug 6, 2009

Un accueil magnifique, personnalisé. Une personne très attachante qui s'est mise en mille pour nous. Un grand moment. Merci Noreen. more

Write Your Own Hotel Review

Been to Mystical Rose Bed and Breakfast? Share your experiences!

Write a Review
Add Photos
Add Videos
Travelers Who Viewed This Property Also Viewed
Location: Killarney, County Kerry
$95*
Check Rates ABS:HotelCheckRates-d569960?src=Hotel_Review&fromServlet=Hotel_Review&Action=QC_AlsoViewed
*Average price

Do You Own Mystical Rose Bed and Breakfast?

TripAdvisor has free and easy resources to help you enhance your listing and promote your property.

  • Start Here - Visit Your Owners' Center
  • Add Photo
  • Add Video

Mystical Rose Bed and Breakfast Address

Woodlawn | Woodlawn, Killarney, Ireland
Killarney B&Bs / Inns Mystical Rose Bed and Breakfast
4.5/5.0
9
P #',159>BFJNRVZ^bfjnrvz~_WebResourceResponse_:http://cdn.tripadvisor.com/js2/tripadvisor-v3138510021a.jsObplist00 X$versionT$topY$archiverX$objects_WebResourceResponse_NSKeyedArchiver$ &,-345STUVWXYZ[\]^_`abcdefghijkopU$null  !"#$%V$classR$3R$8S$10S$11R$5R$6R$4R$7R$2R$9R$0R$1#" '()$+WNS.base[NS.relative_:http://cdn.tripadvisor.com/js2/tripadvisor-v3138510021a.js./01X$classesZ$classname12UNSURLXNSObject#Ag] 6789FWNS.keysZNS.objects!:;<=>?@ABCDE GHIJKLMNOPQR ]Last-Modified]Cache-ControlWExpires]Accept-Ranges_Content-EncodingVServer\Content-Type^Content-LengthTDateTEtagZConnectionTVary_Mon, 09 Nov 2009 12:52:39 GMT^max-age=190083_Wed, 18 Nov 2009 15:41:59 GMTUbytesTgzipVApache_application/x-javascriptU79847_Mon, 16 Nov 2009 10:53:56 GMTW"494bc"Zkeep-alive_Accept-Encoding./lmmn2_NSMutableDictionary\NSDictionary./qrrs2_NSHTTPURLResponse]NSURLResponse)27:PRd OT]hkqz07DSX]hm"49=S`ejnt_application/x-javascriptO//MooTools, My Object Oriented Javascript Tools. Copyright (c) 2006-2007 Valerio Proietti, , 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-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";}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;C0&&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<\/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=(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]B.left&&A.xB.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=/]*>([\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;D0));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 . 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 . Returns: An array containing all the values of the hash */ values: function(){ var values = []; for (var property in this.obj) values.push(this.obj[property]); return values; } }); /* Section: Utility Functions */ /* Function: $H Shortcut to create a Hash from an Object. */ function $H(obj){ return new Hash(obj); };/** * @class * @name Hash */ Hash.implement({ /** * Converts this Hash to an array by calling a function once for each key-value pair. * @param {Function} fn The function to call for each key-value pair * @returns {Array} The resulting array. */ collect: function(fn) { var result = []; this.each(function(v, k){ result.push(fn(v, k)); }); return result; }, /** * Modifies this Hash by calling a function once for each key-value pair and updating the value * to the return value of the function. * @example * Uppercase all values: * myHash.map(function(v, k) { return v.toUpperCase(); }); * @param {Function} fn The function to call for each key-value pair * @returns {Hash} This Hash */ map: function(fn) { this.keys().each(function(k) { this.set(k, fn(this.get(k), k)); }, this); return this; } });if (!$defined(IS_DEBUG)) var IS_DEBUG = false; if (!$defined(VERSION_MAP)) var VERSION_MAP = {}; if (!$defined(CDNHOST)) var CDNHOST = ''; /** * Top level namespace. * @namespace */ var ta = {}; /** * Floating layer support. * @namespace */ ta.overlays = {}; /** * Google Maps extensions and related. * @namespace */ ta.maps = {}; /** Shared functionality @namespace */ ta.common = {}; ta.phac = {}; /** Servlet specific support functions. @namespace */ ta.servlet = {}; /** * Support classes. * @namespace */ ta.support = {}; /** * Utililty methods and classes. * @namespace */ ta.util = {}; /** * Common UI widgets. * @namespace */ ta.widgets = {}; /** Commerce related methods and classes. @namespace */ ta.commerce = {}; /* Event Delegation */ /** * Call the specified behavior handler function. * @example onclick="ta.call('ta.namespace.Class.function', event)" * @example ta.namespace.Class.function = function(evnt, elmt); * @param {String} funcName Name of the function to call * @param {Event} evnt The event * @param {Elmt} [elmt] 'this' * @returns {boolean} The return value of the function. */ ta.call = function(funcName, evnt, elmt) { if (!$defined(evnt)) { if (IS_DEBUG) alert("You must pass the event.\n\nUsage:\nta.call('ta.namespace.Class.function', event);"); return false; } if (!/^ta\.[\w\d\.]+$/.test(funcName)) { if (IS_DEBUG) alert("Invalid function name. Must be of the form: ta.namespace.Class.function"); return false; } var e = new Event(evnt || window.event); if (!elmt) elmt = $(e.target); // try to find and run the function try { var fn = eval(funcName); if ($defined(fn) && $type(fn) == "function") return fn(e, elmt); } catch (_ex) { if (IS_DEBUG) alert("ERROR:\n"+_ex.message); // do nothing } // function not available, try to load dependency e.preventDefault(); // function deferred, prevent default event if (ta.load(funcName.split('.').slice(0,-1))) { ta.util.pending.waitForHandler(funcName.split('.').slice(-1), funcName, e, elmt); } return false; } /** * Dynamically loads the javascript necessary to include the given class. * @param {String} cl Name of class to load */ ta.load = function(cl) { if ($type(cl) == "string") cl = cl.split('.'); var file = cl.join('-')+'.js'; if (!VERSION_MAP[file]) file = cl.slice(0,-1).join('-')+'.js'; if (!VERSION_MAP[file]) { return false; } return ta.loadFile(file); } /** * Dynamically loads a javascript file. File must be defined in the VERSION_MAP, usually by specifying * $onDemandScript in Velocity. * @example #set( $onDemandScript = ['ta-maps.js'] ) * @param {String} file Name of file to load * @returns {Boolean} true if file can be loaded, false if not */ ta.loadFile = function(file) { if (!VERSION_MAP[file]) { if (IS_DEBUG) alert("File not found in version map: "+file); return false; } file = VERSION_MAP[file]; if (IS_DEBUG) file += "?nocache=" + new Date().getTime(); new Asset.javascript(file); return true; } /* On Load Queue */ /** * @private */ ta._loadQ = []; /** * Add a function to be run when the page loads. * @param {function} fn function to run */ ta.queueForLoad = function(fn) { ta._loadQ.push(fn); } /** * Runs any functions in the on load queue. */ ta.onLoad = function() { for (var i = 0; i < ta._loadQ.length; i++) { ta._loadQ[i](); } } window.addEvent('load', ta.onLoad); /* Variable Store */ /** * @private */ ta._store = new Hash(); /** * Stores a variable in the variable store. * @param {String} name variable name * @param {mixed} val value */ ta.store = function(name, val) { ta._store.set(name, val); } /** * Stores a variable in the variable store. If the variable already exists and is an array, appends * val to the end of the array. If it is not an array, it is converted to one with both values. * @param {String} name variable name * @param {mixed} val value */ ta.keep = function(name, val){ var k = []; var x = ta.retrieve(name); if (x){ if (x instanceof Array) k = x; else k.push(x); } if (val instanceof Array) k = k.merge(val); else k.push(val); ta.store(name, k); } /** * Check to see if a variable is defined. * @param {String} name variable name * @returns {boolean} true if variable exists * @retruns {boolean} false if variable is not present */ ta.has = function(name) { return ta._store.hasKey(name); } /** * Return the value of a variable. * @param {String} name variable name * @returns {mixed} the variable value */ ta.retrieve = function(name) { return ta._store.get(name); } /** * Return the value of a variable as an integer. * @param {String} name variable name * @param {integer} [dfltVal=0] return value if parsing fails * @returns {integer} the variable value as an int * @returns {mixed} dfltVal */ ta.asInt = function(name, dfltVal) { var i = parseInt(ta.retrieve(name)); return isNaN(i) ? (dfltVal || 0) : i; } /** * Return the value of a variable as a float. * @param {String} name variable name * @param {float} [dfltVal=0] return value if parsing fails * @returns {float} the variable value as a float * @returns {mixed} dfltVal */ ta.asFloat = function(name, dfltVal) { var f = parseFloat(ta.retrieve(name)); return isNaN(f) ? (dfltVal || 0) : f; } /** * Removes a variable from the variable store and returns it. * @param {String} name variable name * @returns {mixed} the variable */ ta.remove = function(name) { var tmp = ta._store.get(name); ta._store.remove(name); return tmp; } /** @namespace */ ta.util.cookie = {}; /** Standard call for setting the PID based on a click action. @example Click @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) { window.location = asdf(str); }, asdfPopup: function(str) { ta.servlet.Popup.open(asdf(str)); }, asdfPopupEmail: function(str) { ta.servlet.Popup.open(asdf(str),'email',580,460,30,25); }, asdfPopupGuide: function(str) { ta.servlet.Popup.open(asdf(str),'dest_guide',565,700,(screen.width-700)/2,(screen.height-600)/2); }, asdfPopupTerms: function(str) { ta.servlet.Popup.open(asdf(str),'terms',300,300,30,25); } } function getOffset(c){ if (c >= 97 && c<=122) return c-61; if (c >= 65 && c<=90) return c-55; if (c >= 48 && c<=71) return c-48; return -1; } function asdf(sInput){ var 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 0) { elmt.form.submit(); } }, clearOnFocus: function(event, elmt) { if (elmt.value == elmt.defaultValue) { elmt.value = ''; elmt.removeClass('focusClear'); } } };ta.common.flyout = { /** * Creates the Internation Sites overlay. * * @param evnt click event * @param elmt the LI */ intlPopup: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; if (!$defined(flagsUrl) || flagsUrl == ''){ flagsUrl = document.location.pathname; } var uri = '/StaticVelocityPage?t=intSitesFly.vm<=evt&footerFlagFormat=' + footerFlagFormat + '&flagsURL=' + encodeURIComponent(flagsUrl); if (document.location.search != '') uri += encodeURIComponent(document.location.search); if (document.location.hash != '') uri += encodeURIComponent(document.location.hash); if (window['modelGeoId']) uri += '&geo=' + modelGeoId; if (window['flagsSettings']) { for(var k = 0; k < flagsSettings.length; k++) { uri += '&' + flagsSettings[k] + '=true'; } } new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, remoteContent: uri }, elmt); return false; }, /** * "What's This?" Top Value overlay */ whatIsTopValueFlyout:function(evnt, elmt) { evnt.stop(); elmt.onclick = null; // grab the type class var typeClass = elmt.className.match(/\sb?(bv\S*)/); typeClass = typeClass.length > 0 ? typeClass[1] : null; if (typeClass) { new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, content: document.getElement('.'+typeClass+'Info').innerHTML }, elmt); new Ajax('/ActionRecord').request(); } }, /** * Create the Vacation Rentals Owner Type overlay. */ flipKeyOwnerTypeFlyout:function(evnt, elmt) { evnt.stop(); elmt.onclick = null; // grab the type class var typeClass = elmt.className.match(/\sb?(vr\S*)/); typeClass = typeClass.length > 0 ? typeClass[1] : null; if (typeClass) { new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, content: document.getElement('.'+typeClass+'Info').innerHTML }, elmt); } }, /** * Create the Vacation Rentals "What to Expect" overlay. */ vrWhatToExpectFlyout:function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, remoteContent: '/vpages/vacation_renters_tips.html' }, elmt); return false; }, inlineReviewFraudFlyout: function(event, element) { if (!element.flyout) { var g = $('geo'); var d = $('detail'); var params = (g || d) ? ("&g="+g.value+"&d="+d.value) : ""; element.flyout = new Flyout(element, {showArrow: false, offsets:{x:-300, y:15}, remoteContent: '/vpages/fraud.html?lt=evt'+params, flyoutClass: 'fraud'}); element.flyout.toggle(event); } }, /** * deprecated */ destinationsPopup: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, remoteContent: '/vpages/destinationsFly.html?lt=evt' // flyoutClass: 'flyoutMapContents' }, elmt); }, /** * deprecated */ funStuffPopup: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, remoteContent: '/vpages/funStuffFly.html?lt=evt' }, elmt); }, /** * Creates the Member Profile overlay. */ memberOverlay: function(evnt, elmt) { evnt.stop(); elmt.onmouseover = null;ta.overlays.Factory.relBelowH_Media var uid = ""; var catid = ""; if (elmt.id.match(/\bUID_([0-9A-FUS-]+)\b/)) { uid = RegExp.$1; } else if (elmt.id.match(/\bCATID_(\d+)\b/)) { catid = RegExp.$1; } new Asset.css(memoverlayCSS); new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_HOVER, pinnable: ta.overlays.PINNABLE_CLICK, showCloseButton: true, remoteContent: '/MemberOverlay?uid='+uid+'&c='+catid }, elmt); return false; }, guideOverlay: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; var gid = ""; if (elmt.id) gid = elmt.id; new Asset.css(guideoverlayCSS); new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK, remoteContent: '/vpages/travelGuidesFly.html?gid='+gid }, elmt); return false; }, /** * Create Best Value overlay. */ bestValue: function(evnt, elmt) { evnt.stop(); elmt.onmouseover = null; new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_HOVER, remoteContent: '/vpages/bestValueFly.html?log=false' }, elmt); return false; }, recordPropertyTypeClick: function(event, elmt) { if (propertyTypeClicked == false) { new Ajax("/ActionRecord?action=propertyTypeClicked").request(); propertyTypeClicked = true; } } } ta.servlet.Inspiration = { sortPopularity: function(event, elmt) { sort('popularity'); }, sortAlphabetical: function(event, elmt) { sort('alphabetical'); } }; ta.servlet.Reviews = { findLink: function(event, elmt) { // if link has a class starting with t, take rest of class name // as the id of link and follow that link if (/\bt([\w\d]+)\b/.test(elmt.className)) window.location = $(RegExp.$1).href; }, mtSubmitOnClick: function(event, elmt) { elmt.form.submit(); } }; ta.servlet.Location = { /** * function to switch km distances to mi distances. Used for nearby locations. */ showMi: function() { $$('#NEARBY .milesOption').each(function(item, index) { item.checked = true; }); $$('#NEARBY .milesLabel').each(function(item, index) { $(item).addClass('selected'); }); $$('#NEARBY .kmLabel').each(function(item, index) { $(item).removeClass('selected'); }); $$('#NEARBY .distanceMiles').show(); $$('#NEARBY .distanceKm').hide(); Cookie.set(distanceUnitsCookieName, 0); }, /** * function to switch mi distances to km distances. Used for nearby locations. */ showKm: function() { $$('#NEARBY .kmOption').each(function(item, index) { item.checked = true; }); $$('#NEARBY .milesLabel').each(function(item, index) { $(item).removeClass('selected'); }); $$('#NEARBY .kmLabel').each(function(item, index) { $(item).addClass('selected'); }); $$('#NEARBY .distanceMiles').hide(); $$('#NEARBY .distanceKm').show(); Cookie.set(distanceUnitsCookieName, 1); } } ta.servlet.Member = { memberBenefitsSubscribe: function(event) { processMOSubscribeRequest(event); return false; }, memberGuideRequest: function(event) { processGuideRequest(event); }, memberTOGForm: function(event) { processTOGForm(event); } }; ta.servlet.Misc = { /** * callback for CIV selector on FunStuff page */ civRedirOnChange: function(event, element) { element = $('civSelect'); ta.servlet.Misc.openValueInNew.apply(element, [ event ]); return false; }, /** * callback for LP selector on FunStuff page */ lpRedirOnChange: function(event, element) { element = $('localPicksSelect'); ta.servlet.Misc.openValueInNew.apply(element, [ event ]); return false; }, /** * get value of a select and use it to open a new window */ openValueInNew: function(e) { // call bound to a SELECT var uri = this.options[this.selectedIndex].value; if (uri.length == 0) return; var parts = uri.match(/(\d+):(.*)/); if (parts) { setPID(parseInt(parts[1])); uri = parts[2]; } var w = window.open(uri, "p"+window.name+(popupIndex++)); if (w != null) { w.opener = self; w.focus(); } } }; ta.servlet.Tourism = { bbOfferClicked: function(event, element) { event.preventDefault(); element = $(element); if (element.getTag() != 'tr' || !element.hasClass('offerRow')) { element = element.getParent('tr.offerRow'); } offerLink = element.getElement('a'); popCommFN = linkMap['js_popComm']; popCommFN(offerLink,event); } }; /** * Popup related functions. * @namespace */ ta.servlet.Popup = { index: 0, /** * Opens a popup window. Note that the allOptions option takes precedense over noScroll. * @param {String} url Location to open in the new window * @param {String} [name] Name of the new window * @param {integer} [width] Width of the new window * @param {integer} [height] Height of the new window * @param {integer} [x] X position * @param {integer} [y] Y position * @param {boolean} [noScroll] If false, set turns all options off, if true only scroll bars are enabled * @param {boolean} [allOptions=false] If true all options are turned on, false uses browser defaults */ open: function(url, name, width, height, x, y, noScroll, allOptions) { var ops = ""; if (url.indexOf("p=HotelsCom") > -1) { width = Math.max(width, 960); height = Math.max(height, window.getHeight(), 600); } if (width) ops += ",width="+width; if (height) ops += ",height="+height; if (x) ops += ",screenX="+x+",left="+x; if (y) ops += ",screenY="+y+",top="+y; if (ops != "") { if (allOptions) { ops = "toolbar=1,resizable=1,menubar=1,location=1,status=1,scrollbars=1" + ops; } else { ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=" + (noScroll ? 0 : 1) + ops; } } var w = window.open(url, name || "p"+window.name+(ta.servlet.Popup.index++), ops); if (w != null) { try {w.opener = self;} catch (exc) {} w.focus(); return w; } }, /** * Opens a check rates popup window using the href of the element. */ checkrates: function(evnt, elmt) { evnt.preventDefault(); if (elmt.getTag() != 'a') elmt = elmt.getParent('a'); ta.servlet.Popup.open(elmt.href, 'cr', 245, 610, 5, 5); }, /** * Opens a sponsored check rates window. */ sponsorCheckRates: function(evnt, elmt) { evnt.preventDefault(); if (elmt.getTag() != 'span') elmt = elmt.getParent('span'); var accomId = elmt.className.match(/id_(\d+)/)[1]; var srcUrl = "/HotelDateSearch?d=" + accomId + "&fromPop=false&sponsid=" + ta.retrieve('sponsor.id') + '&area=QC_Button_Map&from=HACSearchButton&returnTo=__2F__GMapsLocationController&sponsdet=mapsHacBottom'; ta.servlet.Popup.open(srcUrl, 'cr', 245, 610, 5, 5); }, /** * Opens a terms window. */ termsPopup: function(event, elmt) { popup.apply(elmt, [event, 'terms', 300, 300, 30, 25]); return false; }, /** * applies to a link in a popup window - opens the url in the parent * AND closes the popup */ openInParent: function(event, elmt) { var w = false; if (window.top && window.top.opener) w = window.top; else if (window.opener) w = window; if (!w) return true; var url = ''; if(elmt.getTag() == 'form') { url = elmt.action + '?' + elmt.toQueryString(); } else if (elmt.getTag() == 'a') { url = elmt.href; } else { elmt = elmt.getParent('a'); url = elmt.href; } if(url != '') { if (event) event.preventDefault(); w.opener.location = url; w.close(); } }, /** * Opens an appropriate sized window for a photo gallery. * The url of the photo viewer needs to be supplied. */ photoGallery: function(event, elmt) { if (elmt.getTag() != 'a'){ elmt = elmt.getParent('a'); } var url = elmt.className.match(/u_([^ ]*)/)[1]; // Get the photo id for the cookie var id = elmt.className.match(/#([0-9]+)/)[1]; if (!id) { id = "1"; } // Set a temp cookie to indicate a popup Cookie.set("PhotoPop", id, {domain: cookieDomain}); // No duration, will be removed by server // Open the window var w = ta.servlet.Popup.open(url, 'media', 780, 800); // If we have a window then try to display the right photo (if the window was already open it may be wrong) if (w && w.showImage) { w.showImage(id); } return false; }, /** * Opens an AIM link with larger-than-usual popup window (pool test) */ openAIMLink:function(event,elmt) { new Event(event).preventDefault(); var url; if (elmt.getTag() == 'a') { url = elmt.href; } else if (elmt.getParent().getTag() == 'a'){ url = elmt.getParent().href; } else { return; } ta.servlet.Popup.open(url,null,980,800); }, /** * */ voviciSurveyPopunder:function(event,elmt) { new Event(event).preventDefault(); var url; if (elmt.getTag() == 'a') { url = elmt.href; } else if (elmt.getParent().getTag() == 'a'){ url = elmt.getParent().href; } else { return; } // record the action and pid new Ajax("/ActionRecord?action=AcceptSatisfactionSurvey&pid=1798").request(); // supress all other popunders for the rest of the session var commercePopunder = Cookie.getRaw('CommercePopunder'); if (!commercePopunder || (commercePopunder != 'SuppressAll')) { Cookie.set("CommercePopunder", "SuppressAll", {domain: cookieDomain}); // no duration = session cookie } ta.servlet.Popup.voviciPopunderLaunch(url); }, voviciPopunderLaunch:function(voviciUrl) { var popunderSlots = ta.retrieve("checkrates.popunderSlots"); if(!popunderSlots) { ta.commerce.CROverlay.makePopunderSlots(); popunderSlots = ta.retrieve("checkrates.popunderSlots"); } var popunderslot = ta.commerce.CROverlay.getOpenSlot(0, popunderSlots); var surveyWindow = window.open("", 'voviciSurvey', "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=1,width="+popunderslot.tw+",height="+popunderslot.th+",left="+popunderslot.tx+",top="+popunderslot.ty); if(surveyWindow && ta.commerce.CROverlay.isTAWindow(surveyWindow) ) { surveyWindow.blur(); window.focus(); surveyWindow.location = voviciUrl; popunderslot.occupied = surveyWindow; surveyWindow.opener = self; surveyWindow.moveTo(popunderslot.x, popunderslot.y); surveyWindow.resizeTo(780,670); } else if( surveyWindow ) //window already used for commerce { var unusedPopunderCount = ta.retrieve("checkrates.unusedPopunderCount"); if(!unusedPopunderCount) { unusedPopunderCount = 0; } ta.store("checkrates.unusedPopunderCount", ++unusedPopunderCount); if(unusedPopunderCount < 20) { this.voviciPopunderLaunch(voviciUrl); } } } }; ta.servlet.Hotel = { /** * Start timer for recording hover over amenity. Used in amenity bar. */ amenityMouseOver: function(event, elmt) { // if we haven't already recorded a hover for this page view... if (amenityFiredHoverEvent == false) { // set a timer at 750ms to record the user hovering elmt.hoverTimeout = setTimeout ("recordAmenityIconHover()", 750); } }, /** * Clear timer for recording hover over amenity. Used in amenity bar. */ amenityMouseOut: function(event, elmt) { // stop the timeout clearTimeout (elmt.hoverTimeout); // remove any references to it elmt.hoverTimeout = null; }, detailsTabClicked: function(event, elmt) { var tab = $('tabDetails'); showTab(locationId,'tabDetails', tab); }, photosTabClicked: function(event, elmt) { var tab = $('tabPhotos'); showTab(locationId,'tabPhotos',tab); }, mapTabClicked: function(event, elmt) { var tab = $('tabMap'); showTab(locationId,'tabMap',tab); }, ratingsTabClicked: function(event, elmt) { var tab = $('tabRatings'); showTab(locationId,'tabRatings',tab); }, setPid:function(pid) { Cookie.set('NPID', pid, {domain: cookieDomain, time: 5}); return true; } }; /** @namespace */ ta.commerce.CROverlay = { /** * Callback for when the check rates button is pressed. */ Vendor: function(lName, lCheckboxID, lUrl, lVendorName, lAddOffer, lPropId) { this.name = lName; this.checkbox = lCheckboxID; this.url = lUrl; this.window = null; this.lastDates = ""; this.slot = null; this.bOpened = false; this.vendorName = lVendorName; this.additionalOffer = lAddOffer; this.propId = lPropId; }, createPairedCalendar: function(evnt, elmt){ new Event(evnt).preventDefault(); new ta.overlays.PairedCalendar(elmt, { validate: true, onUpdate: ta.commerce.CROverlay.updateInlineCalendar }); }, updateInlineCalendar: function(paired){ var form = $$('#CHECK_RATES_CONT form')[0]; if (!form) return; if (!form['calendar']) form.calendar = new ta.overlays.PairedCalendar(form, { validate:true, onUpdate: ta.commerce.CROverlay.updateInlineCalendar }); if (form.calendar != paired) { form.calendar.before.calendar.selectedDate = paired.before.calendar.selectedDate; form.calendar.after.calendar.selectedDate = paired.after.calendar.selectedDate; form.calendar.before.updateFields(); form.calendar.after.updateFields(); } /* part of second ICR test, can be discarded if no second ICR */ var syncForm = $$('#CHECK_RATES_CONT_SYNC form')[0]; if (!syncForm) return; if (!syncForm['calendar']) syncForm.calendar = new ta.overlays.PairedCalendar(syncForm, { validate:true, onUpdate: ta.commerce.CROverlay.updateInlineCalendar }); if (syncForm.calendar == paired) return; syncForm.calendar.before.calendar.selectedDate = form.calendar.before.calendar.selectedDate; syncForm.calendar.after.calendar.selectedDate = form.calendar.after.calendar.selectedDate; syncForm.calendar.before.updateFields(); syncForm.calendar.after.updateFields(); }, updateInlineFormElement: function(elmt){ elmt = $(elmt); var elemTag = elmt.getTag().toLowerCase(); if(elemTag == 'li') { this._updateInlineFormElement_CB(elmt, false); } else if(elemTag == 'select') { this._updateInlineFormElement_Sel(elmt, false); } else if(elemTag == 'input') { this._updateInlineFormElement_In(elmt, false); } }, /** * Callback for when the check rates button is pressed. */ checkRatesClicked: function(event, elmt) { ta.commerce.CROverlay.displayVendors(elmt); }, /** * Open a series of windows with rate information for each selected vendor */ displayVendors: function(elmt) { var crOverlayElem = $E('.overlay'); if(crOverlayElem && crOverlayElem.overlay && crOverlayElem.overlay.pin) { crOverlayElem.overlay.pin(); } var vendorSetId = false; var vendorSet = false; if(vendorSetId = elmt.className.match(/id_(\d+)/)) { vendorSetId = vendorSetId[1]; var vends = ta.retrieve("checkrates.vendors." + vendorSetId); if(vends) { vendorSet = vends; } } var formElem = elmt.getParent('form'); if (!vendorSetId || !vendorSet || !this.validateDates(formElem, vendorSetId)) { return; } else { formElem.getElement('span.error_msg').hide(); } try { this.popwindows(elmt, vendorSet, vendorSetId); this.addBoomerangTag(elmt, vendorSetId); } catch(e) { } return false; }, validateDates: function(formElem, vendorSetId) { //FIX FOR _POP var checkInElem = $('checkIn_' + vendorSetId); var checkOutElem = $('checkOut_' + vendorSetId); // doesn't have calendar, nevermind... if (!formElem || !checkInElem || !checkOutElem) return true; if( !checkInElem.value || checkInElem.value.match(/([A-Za-z])/) || !checkOutElem.value || checkOutElem.value.match(/([A-Za-z])/) ) { formElem.getElement('span.error_msg').setContent(sInvalidDates).show(); return false; } // never used the calendar, assume defaults are OK if (!checkInElem.calendar || !checkOutElem.calendar) return true; var valid = true; var msg; if ((msg = validateDate(checkInElem.calendar)) != null || (msg = validateDate(checkOutElem.calendar)) != null) { valid = false; formElem.getElement('span.error_msg').setContent(msg).show(); } else if (!(checkOutElem.calendar.selectedDate >= checkInElem.calendar.selectedDate)) { valid = false; formElem.getElement('span.error_msg').setContent(sInvalidDates).show(); } return valid; }, popwindows: function (trigger, vendorSet, vendorSetId) { var numChecked = 0; var numToOpen = 0; var numOpened = 0; var numOpen = 0; var numCheckedOrOpen = 0; for (var i=0; i= dCheckout) { alert(js_0003); // "Please choose a check-out date that is at least one day later than your check-in date." return false; } var dTooFar = new Date((new Date()).getTime() + (330 * 86400000)); if (dCheckin > dTooFar || dCheckout > dTooFar) { alert(js_0004); // "Please choose dates that are less than 330 days away." return false; } $("msgbox").setContent(js_0005).show(); // "Searching for deals ... this may take a few moments" return ( "inMonth=" + escape(sInMonthYear[0] + " " + sInMonthYear[1]) + "&inDay=" + escape($('qcInDay_'+vendorSetId).value) + "&outMonth=" + escape(sOutMonthYear[0] + " " + sOutMonthYear[1]) + "&outDay=" + escape($('qcOutDay_'+vendorSetId).value) + "&adults=" + escape($('qcAdults_'+vendorSetId).selectedIndex+1) ); }, popAll: function(trigger, sUserDataVar, vendorSet) { for (var i=0; i vends.length - 2 || usedPopunders > checkedVendors - 2) { return; } //vendor check complete var popunderslot = this.getOpenSlot(0, popunderSlots); if(popunderslot) { var unusedPopunderCount = ta.retrieve("checkrates.unusedPopunderCount"); if(!unusedPopunderCount) { unusedPopunderCount = 0; } var windowName = "UPC_" + unusedPopunderCount; ta.store("checkrates.unusedPopunderCount", ++unusedPopunderCount); var cpu_win = window.open("", windowName, "toolbar=1,location=1,directories=1,status=1,menubar=1,resizable=1,copyhistory=1,scrollbars=1,width="+popunderslot.tw+",height="+popunderslot.th+",left="+popunderslot.tx+",top="+popunderslot.ty); if(cpu_win && this.isTAWindow(cpu_win) ) { cpu_win.blur(); window.focus(); cpu_win.location = popunder_uri; popunderslot.occupied = cpu_win; cpu_win.opener = self; cpu_win.moveTo(popunderslot.x, popunderslot.y); cpu_win.resizeTo(popunderslot.w,popunderslot.h); } else if( cpu_win && unusedPopunderCount < 20) //window already used for commerce { this.popunderLaunch(popunder_uri, vendorSetId); } } }, Slot: function (lx, ly, lw, lh){ this.x = lx; this.y = ly; this.w = lw; this.h = lh; this.occupied = null; }, makeSlots: function (){ var nW = 525; var nH = 460; var nXI = 24; var nYI = 24; //move all the pop windows away from the dhtml popup var nX = 500; var nY = (window.screenTop || window.screenY); if(screen.width > 1024){ nW = 800; nH = 600; nXI = 80; nYI = 40; } else if(screen.width > 800){ nW = 620; nH = 500; nXI = 60; nYI = 30; } var slots = []; for (var i=0; i<20; i++){ slots[i] = new this.Slot(nX,nY,nW,nH); nX = nX + nXI; nY = nY + nYI; } ta.store("checkrates.slots", slots); }, PopunderSlot: function (x, y, w, h, tx, ty, tw, th){ this.x = x; this.y = y; this.w = w; this.h = h; this.tx = tx; this.ty = ty; this.tw = tw; this.th = th; this.occupied = null; }, makePopunderSlots: function (){ var w = 1; var h = 1; var nW = 620; var nH = 500; var nXI = 24; var nYI = 24; if (window.ie7) {w = 250; h = 100;} if (window.webkit) { w = 85; h = 100;} var nX = (window.screenLeft || window.screenX); var nY = (window.screenTop || window.screenY); if (typeof(nX) == "undefined") { // full screen nX = 0; nY = 0; var x = nX + window.screen.availWidth - w; // bottom-right corner of window var y = nY + window.screen.availHeight - h; } else{ var x = nX + window.getWidth() - w; // bottom-right corner of window var y = nY + window.getHeight() - h; } if(window.ie7) { y = y - 95; x = x + 13; } else if(window.webkit) { y = y - 16; } else { x = 5000; y = 5000; } var popunderSlots = []; var popunderSlotCount = 2; for (var i=0; i=0; i-=1){ if (slotGroup[i].occupied == null){ return slotGroup[i]; } else if (slotGroup[i].occupied.closed){ slotGroup[i].occupied = null; return slotGroup[i]; } } for (var i=n; iTip: Travelers usually check 3 or more sites to find the best price.' ); } }, _isCheckBoxChecked: function(vendorObj, parentOverlay){ var isChecked = false; if(parentOverlay) { var nodeCol = parentOverlay.getElement('input[id^=' + vendorObj.checkbox + ']'); if(nodeCol) { isChecked = nodeCol.checked; } else { var checkBoxSet = ta.retrieve('checkBoxSet_' + vendorObj.propId); isChecked = checkBoxSet[vendorObj.name]; } } return isChecked; }, /** * List of extensions that can be appended to check rates form elements * to trigger syncing with the master ICR box elements * NEED TO BE OF LENGTH 4 */ crSyncExtensions: new Array('_pop', '_icr'), updateCalendarFormElement: function(e,year,month,day){ var cal = currentCalendar; calendarSelect(e,year,month,day); this.updateInlineFormElement(cal.source); var other = cal.before || cal.after; if(other) { this.updateInlineFormElement(other); } }, /* * keep inline CR form element in sync with dhtml version * of the same element * elmt passed in is the dhtml version of element * flip is a toggle to do the opposite (sync the * dhtml with the inline) */ _updateInlineFormElement_CB: function(elmt, flip){ var elemCheckBox = elmt.getElement('input[type=checkbox]'); if(elemCheckBox) { var ext = elemCheckBox.id.substring( elemCheckBox.id.length - 4 ); if(this.crSyncExtensions.contains(ext)) { var inlineCBName = elemCheckBox.id.substring( 0, elemCheckBox.id.length - 4 ); var inlineCheckBox = $(inlineCBName); if(inlineCheckBox) { if(flip) { elemCheckBox.checked = inlineCheckBox.checked; } else { inlineCheckBox.checked = elemCheckBox.checked; var syncForm = $$('#CHECK_RATES_CONT_SYNC form')[0]; if(syncForm) { var syncCB = syncForm.getElement('#' + inlineCBName + "_icr"); syncCB.checked = inlineCheckBox.checked; } } } } else { var inlineCBName = elemCheckBox.id; var inlineCheckBox = $(inlineCBName); this.crSyncExtensions.each(function(syncExt){ var syncElem = inlineCBName + syncExt; syncElem = $(syncElem); if(syncElem) { syncElem.checked = inlineCheckBox.checked; } }); } } }, _updateInlineFormElement_Sel: function(elmt, flip){ var ext = elmt.id.substring( elmt.id.length - 4 ); if(this.crSyncExtensions.contains(ext)) { var inlineSelName = elmt.id.substring( 0, elmt.id.length - 4 ); var inlineSelect = $(inlineSelName); if(inlineSelect) { if(flip) { elmt.selectedIndex = inlineSelect.selectedIndex; } else { inlineSelect.selectedIndex = elmt.selectedIndex; } } this._updatedInlineFormElement_Sel_Helper(inlineSelName); } else { this._updatedInlineFormElement_Sel_Helper(elmt.id); } }, _updatedInlineFormElement_Sel_Helper: function(inlineSelName){ var inlineSelect = $(inlineSelName); this.crSyncExtensions.each(function(syncExt){ var syncElem = inlineSelName + syncExt; syncElem = $(syncElem); if(syncElem) { syncElem.selectedIndex = inlineSelect.selectedIndex; } }); }, _updateInlineFormElement_In: function(elmt, flip){ if(elmt && elmt.id.indexOf('_pop') != -1) { var inlineCalName = elmt.id.substring( 0, elmt.id.length - 4 ); var inlineCal = $(inlineCalName); if(inlineCal) { if(flip) { elmt.value = inlineCal.value; } else { inlineCal.value = elmt.value; } var inlineCalLocId = inlineCal.id.match(/_(\d+)/); if(inlineCalLocId) { inlineCalLocId = inlineCalLocId[1]; var calType = 'qcIn'; if(elmt.id.indexOf('checkOut') != -1) { calType = 'qcOut'; } var inlineDay = $(calType + 'Day_' + inlineCalLocId); var overlayDay = $(calType + 'Day_' + inlineCalLocId + '_pop'); if(inlineDay && overlayDay) { if(flip) { overlayDay.value = inlineDay.value; } else { inlineDay.value = overlayDay.value; } } var inlineMonth = $(calType + 'Month_' + inlineCalLocId); var overlayMonth = $(calType + 'Month_' + inlineCalLocId + '_pop'); if(inlineMonth && overlayMonth) { if(flip) { overlayMonth.value = inlineMonth.value; } else { inlineMonth.value = overlayMonth.value; } } } } } }, /* * Function to keep check rates overlays in sync * with the inline check rates box * 'this' inside the function is bound to the overlay */ commCopyUpdate: function(){ if(!this.onReadyFired) { this.onReadyFired = true; this.addEvent('onShow', ta.commerce.CROverlay.commCopyUpdate); } if(this.inner) { var uList = this.inner.getElement('ul'); if(uList) { uList.getElements('li').each( function(listItem){ ta.commerce.CROverlay._updateInlineFormElement_CB(listItem, true); } ); } var crSel = this.inner.getElement('select'); if(crSel) { ta.commerce.CROverlay._updateInlineFormElement_Sel(crSel, true); } var ciCal = this.inner.getElement('input.checkIn'); if(ciCal) { ta.commerce.CROverlay._updateInlineFormElement_In(ciCal, true); } var coCal = this.inner.getElement('input.checkOut'); if(coCal) { ta.commerce.CROverlay._updateInlineFormElement_In(coCal, true); } } }, /** * Pool test: checking an ICR checkbox pops the TPW window * with the label's provider selected */ crLabelClick: function(evnt, checkbox){ var corCB = $(checkbox.htmlFor); if(corCB) { corCB.checked = true; var e = new Event(evnt || window.event); e.stop(); var topForm = $(corCB).getParent('form'); if(topForm) { var corCRButton = $(topForm).getElement('.check img'); if(corCRButton) { var corCBAuthor = corCB.id.substr(0, corCB.id.lastIndexOf('_')); ta.commerce.commerceHelper.openProviderWindow(corCRButton, corCBAuthor); } } } } //update commas if you change the final function in this namespace }; /** * A scrolling, multi-month calendar. * @class * * @option {Date} [selectedDate] Currently selected date * @option {Date} [firstDate] First valid date, anything before it will be disabled * @option {Date} [lastDate] Last valid date, anything after it will be disabled * @option {boolean} [dualCalendar=true] Show two months if true, one if false * @option {Array} [invalidDates] List of invalid dates or date ranges. If it is a date range then we * are expecting a sub-array of size 2. All these dates will be replaced with a red 'X'. Note * that dates may be in timestamp format (i.e. number of milliseconds). * @option {boolean} [useLinks=true] If each valid date should be a link * @option {boolean} [dayNames=false] show abbreviated names if true (e.g. Mon), or letter if false (e.g. M) * @option {Function} [formatter] Function to select cell contents for each day (only used when embedded) * @option {integer} [offsetOther=0] Padding between months if showing two months (only used when embedded) * @option {String} choiceLink name of function to call when user clicks on a date in the calendar * @option {String} prevAction name of function to call when user clicks on previous button * @option {String} nextAction name of function to call when user clicks on next button */ ta.widgets.Calendar = new Class({ options: { selectedDate: null, firstDate: null, lastDate: null, dualCalendar: true, invalidDates: null, useLinks: true, dayNames: false, formatter: null, offsetOther: 0, choiceLink: null, prevAction: null, nextAction: null }, /** * @param {Object} options Options, see below. */ initialize: function(options){ this.setOptions(options); if (!this.options.firstDate) this.options.firstDate = new Date(); if (!this.options.lastDate) this.setLastDate(); this.today = new Date(); this.selectedDate = this.options.selectedDate; // force Dates to midnight for easier comparison this.today.setHours(0,0,0,0); if (this.options.firstDate) this.options.firstDate.setHours(0,0,0,0); if (this.options.lastDate) this.options.lastDate.setHours(0,0,0,0); if (this.selectedDate) this.selectedDate.setHours(0,0,0,0); var arr = []; if (this.options.prevAction && this.options.nextAction){ arr.push(''); } this.container = new Element('div', {'class': 'calendar'}); this.container.setHTML(arr.join('')); }, /** * Set the last date to the end of the month, 11 months from today. */ setLastDate: function(){ var d = new Date(); d.setDate(1); if (d.getMonth() == 0) { d.setMonth(11); } else { d.setMonth(d.getMonth()-1); d.setFullYear(d.getFullYear()+1); } this.options.lastDate = d; }, /** * Update the month(s) shown by the calendar. Should be called whenever the date is changed externally. */ update: function(){ if (this.currentMonth) this.removeTable(null, this.currentMonth); if (this.nextMonth) this.removeTable(null, this.nextMonth); var firstMonth = this.selectedDate || this.today; if (this.options.firstDate && firstMonth < this.options.firstDate) firstMonth = this.options.firstDate; if (this.options.dualCalendar) { var testMonth = new Date(firstMonth); testMonth.setDate(ta.util.date.DAYS_IN_MONTH[testMonth.getMonth()]); testMonth.setHours(0,0,0,0); if (testMonth >= this.options.lastDate) { firstMonth = new Date(firstMonth); if (firstMonth.getMonth() == 0) firstMonth.setFullYear(firstMonth.getFullYear()-1,11,1); else firstMonth.setMonth(firstMonth.getMonth()-1,1); } } this.currentMonth = this.createMonth(firstMonth); this.container.appendChild(this.currentMonth.elmt); if (this.options.dualCalendar) { var d = new Date(this.currentMonth.date); var day = d.getDate(); var nextMonth = d.getMonth()+1; if (day > ta.util.date.DAYS_IN_MONTH[nextMonth]) { day = ta.util.date.DAYS_IN_MONTH[nextMonth]; } d.setMonth(nextMonth, day); this.nextMonth = this.createMonth(d); this.container.appendChild(this.nextMonth.elmt); } // set initial state of prev and next buttons this.updatePrevNext(); return this; }, /** * Adjust the left offset of the current and next month (if applicable) */ reposition: function(){ this.currentMonth.elmt.setStyle('left', '0px'); if (this.options.dualCalendar) { this.nextMonth.elmt.setStyle('left', this.currentMonth.elmt.getCoordinates().width + this.options.offsetOther + 'px'); } return this; }, /** * Update the visibility of the prev and next buttons */ updatePrevNext:function(){ var currentPrev = this.currentMonth.date; var currentNext = this.options.dualCalendar ? this.nextMonth.date : this.currentMonth.date; var hidePrev = ta.util.date.inSameMonth(currentPrev, this.options.firstDate); var hideNext = ta.util.date.inSameMonth(currentNext, this.options.lastDate); getChildByClass(this.container,"prev").style.display = (hidePrev ? 'none' : 'block'); getChildByClass(this.container,"next").style.display = (hideNext ? 'none' : 'block'); }, /** * Creates a month object; including start/end dates and the Table object. * @param {Date} d any date in the month */ createMonth: function(d){ var month = {date: d}; // currDate = the date that we use as we step through the month var currDate = new Date(d); currDate.setDate(1); currDate.setHours(0,0,0,0); var currentDay = (currDate.getDay() - jsGlobalDayOffset + 7) % 7; var arr = []; var year = d.getFullYear(); var currentMonth = d.getMonth(); var now = new Date(); var today = ta.util.date.inSameMonth(now,d) ? now.getDate() : -1; var selectedDay = -1; if (this.selectedDate && this.selectedDate.getMonth() == currentMonth) selectedDay = this.selectedDate.getDate(); arr.push(''); for (var i = 0; i < jsGlobalDaysShort.length; i++) { arr.push(''); } arr.push(''); // blanks at beginning of month for (var i = 0; i < currentDay; i++) arr.push('') // 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(''); } if (!inRange) { arr.push('"); } else { if (cname) { arr.push(''); } } // blanks at end of month while (++currentDay < 7) arr.push("") arr.push('
'); arr.push(DATE_FORMAT_MMM_YYYY.replace(/MMM/,jsGlobalMonths[currentMonth]).replace(/YYYY/,year)); arr.push('
'); arr.push(this.options.dayNames ? jsGlobalDaysAbbrev[i] : jsGlobalDaysShort[i]); arr.push('
 
'); arr.push(contents || day); arr.push("'); } else { arr.push(''); } if (isValid && this.options.useLinks && link) { arr.push(''); arr.push(contents); arr.push(''); } else { arr.push(contents); } arr.push(' 
'); month.elmt = new Element('div', {'class': 'month'}).setHTML(arr.join('')); return month; }, /** * Check if this is an invalid date given 'this.options.invalidDates'. * @param {Date} date date to check */ isInvalidDate: function(date){ return ta.util.date.inDateSet(date, this.options.invalidDates); }, /** * Verifies that the selected date of this calendar is equal to or between the first and last dates. * * @returns {boolean} the validity of the selected date */ isValid: function(){ if (!this.selectedDate) return true; return this.selectedDate.getTime() >= this.options.firstDate.getTime() && this.selectedDate.getTime() <= this.options.lastDate.getTime(); }, /** * Moves the calendar back one month. */ prev: function(){ if (this.animating) return; var dc = this.options.dualCalendar; this.animating = true; // calculate the prev month var d = new Date(this.currentMonth.date); var month = d.getMonth()-1; if(month < 0){ month = month + 12; d.setFullYear(d.getFullYear() - 1); } var day = d.getDate(); if (day > ta.util.date.DAYS_IN_MONTH[month]) { day = ta.util.date.DAYS_IN_MONTH[month]; } d.setMonth(month, day); // grab the width pf the current month table var w = this.currentMonth.elmt.getCoordinates().width; // create the new month table, offset it to its starting position (to prevent flicker) // and inject it (we don't have the actual width of the new month table yet so assume // it is about the size of the current month... should be good enough to eliminate // the flicker). var m = this.createMonth(d); m.elmt.style.left = '-' + w + 'px'; m.elmt.inject(this.container.getElement('.navCal'), 'after'); // grab the tables and slide them to the right with an animation var elmts = this.container.getElements('div.month'); var opts = {'0':{left:[-w,0]}, '1':{left:[0,w + this.options.offsetOther]}}; if (dc) opts['2'] = {left:[w + this.options.offsetOther,w*2 + this.options.offsetOther]}; var fx = new Fx.Elements(elmts, { onComplete: this.doneAnimating.bindAsEventListener(this, [this.nextMonth || this.currentMonth]) }); // update the current and next month if (dc) this.nextMonth = this.currentMonth; this.currentMonth = m; // update the state of the prev and next buttons this.updatePrevNext(); // start the animation fx.start(opts); }, /** * Moves the calendar forward one month. */ next: function(){ if (this.animating) return; var dc = this.options.dualCalendar; this.animating = true; // calculate the next month var d = new Date(this.currentMonth.date); var month = d.getMonth() + (dc ? 2 : 1); if(month > 11){ month = month - 12; d.setFullYear(d.getFullYear() + 1); } var day = d.getDate(); if (day > ta.util.date.DAYS_IN_MONTH[month]) { day = ta.util.date.DAYS_IN_MONTH[month]; } d.setMonth(month, day); // grab the width pf the current month table var w = this.currentMonth.elmt.getCoordinates().width; // grab the total width of the calendars (if it is a dual calendar) var totalWidth = w + (dc ? this.nextMonth.elmt.getCoordinates().width : 0) // create the new month table, offset it to its starting position (to prevent flicker) // and inject it var m = this.createMonth(d); m.elmt.style.left = totalWidth + 'px'; m.elmt.injectInside(this.container); // grab the tables and slide them to the left with an animation var elmts = this.container.getElements('div.month'); var opts = {'0':{left:[0,-w]}, '1':{left:[w + this.options.offsetOther,0]}}; if (dc) opts['2'] = {left:[w*2 + this.options.offsetOther,w + this.options.offsetOther]}; var fx = new Fx.Elements(elmts, { onComplete: this.doneAnimating.bindAsEventListener(this, [this.currentMonth]) }); // update the current and next month if (dc){ this.currentMonth = this.nextMonth; this.nextMonth = m; } else { this.currentMonth = m; } // update the state of the prev and next buttons this.updatePrevNext(); // start the animation fx.start(opts); }, /** * Removes the table from the container. Used by prev/next as a bound event listener for when * the animation completes. * @param {Event} e event object, unused * @param {Element} t element that is the month to be removed */ removeTable: function(e, t){ t.elmt.remove(); }, doneAnimating: function(e, t){ this.removeTable(e, t); this.animating = false; }, /** * Called when the user selects a date. Fires any onSelect events. * @param {Event} e event object * @param {Date} d date the user selected */ select: function(e, d){ this.selectedDate = d; this.fireEvent('onSelect', [this, e]); }, /** * Convenience method to get a month near a given month. Checks for boundary conditions. * @param {integer} month starting month * @param {integer} mod amount to alter the number by (positive or negative) */ nearbyMonth: function(month, mod){ var d = month + mod; if (d > 11) d -= 12; else if (d < 0) d += 12; return d; } }); ta.widgets.Calendar.implement(new Events, new Options); /** Never show the backdrop */ ta.overlays.BACKDROP_NEVER = 0; /** Always show the backdrop */ ta.overlays.BACKDROP_ALWAYS = 1; /** * Common base class for all floating overlay elements. A overlay is an element that appears on the page * above all other elements and is absolutely positioned over the document. This class will * automatically place an iframe behind the overlay to block select element in IE6. * @class * * @option {boolean} [showCloseButton=false] Whether or not to show the close button * @option {String} [style=typeO] Class name on container div. * @option {boolean} [backdrop=ta.layers.BACKDROP_NEVER] Should be one of ta.layers.BACKDROP_* */ ta.overlays.Overlay = new Class({ options: { requestData: null, showCloseButton: false, style: 'typeO', backdrop: ta.overlays.BACKDROP_NEVER, isChild: false, remoteContent: null }, /** * @param {Object} options Options, see below. */ initialize: function(options, elmt) { this.setOptions(options); this.source = $(elmt); // state flags this.visible = false; this.backdropOn = false; // bind event handlers this.showHandler = this.show.bindWithEvent(this); this.hideHandler = this.hide.bindWithEvent(this); // setup container this.container = new Element('div', { styles: { position: 'absolute', left: '-999em', top: '-999em', fontSize: '.75em', zIndex: '1001' }, 'class': this.options.style + " overlay" }); this.container.overlay = this; this.inner = new Element('div', { 'class': 'inner' }).injectInside(this.container); if (this.options.showCloseButton) { this.closeBtn = new Element('div', { styles: { position: 'absolute', fontSize: '92.5%', cursor: 'pointer' }, 'class': 'close' }).inject(this.container); this.inner.addClass('withClose'); } // IE6 requires an IFrame shim if (window.ie6) { this.shim = new Element('iframe', { styles: { position: 'absolute', left: '-999em', top: '-999em', border: 'none' } }); } if (this.options.content) { this.inner.setHTML(this.options.content); } else if (this.options.remoteContent) { this.loadRemoteContent(this.options.remoteContent); } this.container.addEvent('trash', this.destroy.bind(this)); }, destroy: function(){ this.container.overlay = null; }, /** * Positions the container. */ position: function() { this.positionShim(); }, /** * Positions the IFrame shim if necessary. */ positionShim: function() { if (!this.shim) return; // a small delay is needed in case the popup styles change its size (function(){ this.shim.injectBefore(this.container); var c = this.container.getCoordinates(); this.shim.setStyles({ left: c.left, top: c.top, width: c.width, height: c.height, zIndex: this.container.getStyle('z-index') - 1 }); }).delay(10, this); }, /** * Adds the container to the DOM and positions it. * @param {Event} evnt The event */ show: function(evnt) { if (this.visible) return; if (evnt) new Event(evnt).preventDefault(); // it is possible for an overlay to spawn multiple child overlays, so find other overlays and // remove them if they aren't the parent overlay // or if they have not been flagged as permanent (like the dhtml popups) - see bug 40975. $$('.overlay').each(function(other){ if (other.overlay == this) return; if (this.parentOverlay && other.overlay == this.parentOverlay) return; if (other.overlay instanceof ta.overlays.PermanentOverlay) return; other.overlay.hide(evnt); }, this); $(document.body).adopt(this.container); this.position(); this.positionShim(); if (this.options.backdrop == ta.overlays.BACKDROP_ALWAYS) this.enableBackdrop(); this.visible = true; this.fireEvent('onShow', this); return this; }, /** * Removes the container from the DOM. * @param {Event} evnt The event */ hide: function(evnt) { if (window.flyout) window.flyout.hide(); if (this.backdropOn) this.disableBackdrop(); if (!this.container.inDocument()) return this; if (this.shim) { this.shim.setStyles({ left: '-999em', top: '-999em' }); if (this.shim.inDocument()) this.shim.remove(); } this.container.setStyles({ left: '-999em', top: '-999em' }).remove(); this.visible = false; this.fireEvent('onHide'); return this; }, /** * Configures the backdrop. */ configureBackdrop: function() { this.backdrop = new Element('div'); this.backdrop.setStyles({ position: 'absolute', left: 0, top: 0, width: window.getScrollWidth(), height: window.getScrollHeight(), backgroundColor: '#000', zIndex: this.options.isChild ? '10000' : '9997' }).setOpacity(0.6); }, /** * update backdrop on show */ updateBackdrop: function() { this.backdrop.setStyles({ width: window.getScrollWidth(), height: window.getScrollHeight() }); }, /** * Adds the opacity backdrop behind this layer. */ enableBackdrop: function() { if (this.backdropOn) return; if (!this.backdrop) this.configureBackdrop(); if(window.ie6) { var container = this.container; $$('select').each(function(elem){ if(!container.hasChild(elem)) { elem.setStyle('visibility', 'hidden'); } else { elem.setStyle('visibility', 'visible'); } }); } ta.store('overlays.current', this); this.updateBackdrop(); if(this.container.inDocument()) this.backdrop.injectBefore(this.container); else this.backdrop.injectBefore(document.body); this.backdropOn = true; this.container.setStyle('z-index', this.options.isChild? '10002' : '9998'); }, /** * Removes the opacity backdrop from behind this layer. */ disableBackdrop: function() { if (!this.backdropOn) return; this.backdrop.remove(); if(ta.retrieve('overlays.current') == this) { ta.remove('overlays.current'); if(window.ie6) { $$('select').setStyle('visibility', 'visible'); } } else { if(window.ie6 && ta.retrieve('overlays.current') && !ta.retrieve('overlays.current').backdropOn) { $$('select').setStyle('visibility', 'visible'); } } this.backdropOn = false; this.container.setStyle('z-index', '1001'); }, /** * Request remote content. */ loadRemoteContent: function(uri, bHideSpinner) { if(!bHideSpinner) { new Asset.image(CDNHOST + '/img2/generic/site/loop.gif', { onload: this.position.bind(this), 'class': 'anim_loop' }).injectInside(this.inner); } new Ajax(ta.util.URL.parse(uri), { data: this.options.requestData, onSuccess: this.loadRemoteSuccess.bind(this), onFailure: this.loadRemoteFailure.bind(this), evalScripts: true }).request(); return this; }, /** * Called when remote content is successfully loaded. * @param {String} txt The remote content */ loadRemoteSuccess: function(txt) { this.cachedWidth = null; this.inner.setHTML(txt); this.position(); if(typeof behavior != 'undefined' && behavior && behavior.apply) { behavior.apply(this.inner); } this.fireEvent('onLoad', this); this.cachedWidth = this.container.getSize().size.x; return this; }, /** * Called when remote content fails to load. */ loadRemoteFailure: function() { this.inner.setText(JS_Ajax_failed); } }); ta.overlays.Overlay.implement(new Options, new Events); /** * An overlay positioned absolutely in the window. * * @option {boolean} [showCloseButton=true] Whether or not to show the close button * @option {integer} [xOffset=0] Left position of overlay * @option {integer} [yOffset=0] Top position of overlay * @option {integer} [toWindow=true] Positions in window if true, on document if false. */ ta.overlays.AbsoluteOverlay = ta.overlays.Overlay.extend({ options: { xOffset: 0, yOffset: 0, toWindow: true }, initialize: function(options, elmt) { options = $pick(options, {}); options.showCloseButton = $pick(options.showCloseButton, true); this.parent(options, elmt); // register for events if (this.options.showCloseButton) { this.closeBtn.addEvent('click', this.hideHandler); } this.show(); }, /** * Positions the container. */ position: function() { this.container.setStyles({ left: this.options.xOffset, top: this.options.yOffset + (this.options.toWindow ? window.getScrollTop() : 0) }); this.positionShim(); } }); /** * An overlay positioned in the center of the window. * * @option {boolean} [showCloseButton=true] Whether or not to show the close button */ ta.overlays.CenteredOverlay = ta.overlays.Overlay.extend({ options: { delayedPosition: false, autoShow: true }, initialize: function(options, elmt) { options = $pick(options, {}); options.showCloseButton = $pick(options.showCloseButton, true); this.parent(options, elmt); // register for events if (this.options.showCloseButton) { this.closeBtn.addEvent('click', this.hideHandler); } if (this.options.autoShow) this.show(); }, /** * Positions the container. */ position: function() { if (this.options.delayedPosition) this._position.delay(10, this); else this._position(); }, _position: function() { var c = this.container.getCoordinates(); this.container.setStyles({ left: Math.max(5,(window.getWidth() - c.width)) / 2 + window.getScrollLeft(), top: Math.max(20, window.getHeight() - c.height) / 2 + window.getScrollTop() }); this.positionShim(); } }); ta.overlays.showInLightbox = function(txt, ops){ return new ta.overlays.CenteredOverlay($merge({backdrop: ta.overlays.BACKDROP_ALWAYS}, ops)).loadRemoteSuccess(txt); }; ta.overlays.loadInLightbox = function(uri){ return new ta.overlays.CenteredOverlay({backdrop: ta.overlays.BACKDROP_ALWAYS}).loadRemoteContent(uri); }; // bug: 38152 // temporary fix to catch any remaining issues, should be removed when all references to "window.lightbox" have been removed. var lightbox = { deactivate: function() { var lb = ta.retrieve('overlays.current'); if (lb) lb.hide(); } }; /** Do not attach show/hide actions. */ ta.overlays.ACTIVATE_NEVER = 0; /** Show the flyout when clicking on the source element */ ta.overlays.ACTIVATE_CLICK = 1; /** Show the flyout when mousing over the source element */ ta.overlays.ACTIVATE_HOVER = 2; /** Show the overlay when the element gains focus. */ ta.overlays.ACTIVATE_FOCUS = 3; /** Do not pin the flyout open */ ta.overlays.PINNABLE_NEVER = 0; /** Always pin the flyout open. Clicking on document will not close the flyout */ ta.overlays.PINNABLE_ALWAYS = 1; /** Pin the flyout when clicking on the source element */ ta.overlays.PINNABLE_CLICK = 2; /** Only show the backdrop when pinned */ ta.overlays.BACKDROP_PINNED = 2; /** * Basic Flyout layer. A flyout is defined as a layer with an arrow pointing to a source element. * @class * * @option {boolean} [activate=ta.layers.ACTIVATE_NEVER] Should be one of ta.layers.ACTIVATE_* * @option {boolean} [pinnable=ta.layers.PINNABLE_NEVER] Should be one of ta.layers.PINNABLE_* * @option {boolean} [autoShow=true] Show overlay upon creation. */ ta.overlays.RelativeOverlay = ta.overlays.Overlay.extend({ options: { activate: ta.overlays.ACTIVATE_NEVER, pinnable: ta.overlays.PINNABLE_NEVER, autoShow: true }, /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { this.parent(options, elmt); this.container.addClass('relative'); var parentOverlay = this.source.getParent('.overlay'); if (parentOverlay) { this.container.setStyle('z-index', parentOverlay.getStyle('z-index')+1); this.parentOverlay = parentOverlay.overlay; } // additional event handlers this.pinHandler = this._pin.bindWithEvent(this); this.hideNowHandler = this.hideNow.bindWithEvent(this); // register for events if (this.options.showCloseButton) { this.closeBtn.addEvent('click', this.hideNowHandler); } // activate type switch (this.options.activate) { case ta.overlays.ACTIVATE_CLICK: this.source.addEvent('click', this.showHandler); break; case ta.overlays.ACTIVATE_HOVER: this.source.addEvent('mouseenter', this.showHandler); this.source.addEvent('mouseleave', this.hideHandler); this.container.addEvent('mouseenter', this.showHandler); this.container.addEvent('mouseleave', this.hideHandler); break; case ta.overlays.ACTIVATE_FOCUS: this.source.addEvent('focus', this.showHandler); break; } // pinnable type switch (this.options.pinnable) { case ta.overlays.PINNABLE_CLICK: this.source.addEvent('click', this.pinHandler); break; } // local contents var contents = this.source.getElement('.overlayContents'); if (contents) { this.inner.setHTML(contents.innerHTML); contents.remove(); this.runBehaviorOnFirstShow = true; } // remote contents var remote = this.source.getElement('.overlaySrc'); if (remote) { if (remote.getTag() == 'a') { this.loadRemoteContent(remote.href); } else { this.loadRemoteContent(remote.getText()); remote.remove(); } } else if (this.source.getTag() == 'a') { this.loadRemoteContent(this.source.href); } if (this.options.autoShow) this.show(); }, /** * Adds the container to the DOM and positions it. * @param {Event} evnt The event */ show: function(evnt) { evnt = evnt || window.event; if (evnt) new Event(evnt).stop(); var vis = this.visible; this.parent(evnt); if (this.options.activate == ta.overlays.ACTIVATE_HOVER && this.hideTimer) { $clear(this.hideTimer); this.hideTimer = null; } if (vis) return; if (this.runBehaviorOnFirstShow){ this.runBehaviorOnFirstShow = false; behavior.apply(this.inner); this.cachedWidth = this.container.getSize().size.x; } switch (this.options.activate) { case ta.overlays.ACTIVATE_HOVER: break; case ta.overlays.ACTIVATE_CLICK: case ta.overlays.ACTIVATE_FOCUS: if (this.options.pinnable == ta.overlays.PINNABLE_NEVER) { document.addEvent('click', this.hideHandler); } break; } return this; }, /** * Removes the container from the DOM. * @param {Event} evnt The event */ hide: function(evnt) { evnt = evnt || window.event; if (!this.visible) return; if (window.flyout) window.flyout.hide(); if (evnt) { var e = new Event(evnt); var eTarg = $(e.target); if (eTarg.getTag() == 'option') return; // event occurred on an open select dropdown if (window.ie && eTarg.getTag() == 'select' && this.container.getElements('select').contains(e)) return; } switch (this.options.activate) { case ta.overlays.ACTIVATE_HOVER: //CHECK TO SEE IF SRC OR CONTAINER CONTAINS EVNT, IF SO IGNORE, IF NOT: HIDE TIMER ROUTINE if (!this.hideTimer) { this.hideTimer = this.hide.delay(250, this); return; } else { this.hideTimer = null; } break; case ta.overlays.ACTIVATE_CLICK: case ta.overlays.ACTIVATE_FOCUS: if (this.options.pinnable == ta.overlays.PINNABLE_NEVER) { document.removeEvent('click', this.hideHandler); } if (evnt && this.container.contains(evnt)) return; break; } return this.hideNow(); }, /** * hack for mootools 1.1 * Called from hide to execute hide after some checks * @param {Event} evnt The event */ hideNow: function(evnt) { if (!this.visible) return this; if (this.backdropOn) this.disableBackdrop(); if (this.shim) { this.shim.setStyles({ left: '-999em', top: '-999em' }).remove(); } this.container.setStyles({ left: '-999em', top: '-999em' }).remove(); this.visible = false; if (this.options.pinnable == ta.overlays.PINNABLE_CLICK) this.unpin(); this.fireEvent('onHide'); return this; }, _pin: function(evnt) { this.pin(); this.toggleBackdrop(true); }, /** * Used when pinnable is set to PIN_CLICK. * @param {Event} evnt The event */ pin: function() { this.source.removeEvent('mouseenter', this.showHandler); this.source.removeEvent('mouseleave', this.hideHandler); this.container.removeEvent('mouseenter', this.showHandler); this.container.removeEvent('mouseleave', this.hideHandler); this.pinned = true; return this; }, toggleBackdrop: function(enable) { if (this.options.backdrop == ta.overlays.BACKDROP_PINNED) this.enableBackdrop(); }, /** * Unpins this flyout, re-adds the mouse enter/leave handlers. */ unpin: function() { this.source.addEvent('mouseenter', this.showHandler); this.source.addEvent('mouseleave', this.hideHandler); this.container.addEvent('mouseenter', this.showHandler); this.container.addEvent('mouseleave', this.hideHandler); if (this.options.backdrop == ta.overlays.BACKDROP_PINNED) this.disableBackdrop(); this.pinned = false; return this; } }); /** * Left/Right Flyout layer. * @class * * @option {boolean} onRight True for flyout on the right, false for left. */ ta.overlays.RelativeOverlayHorizontal = ta.overlays.RelativeOverlay.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { if (!$defined(options.onRight)) { if (IS_DEBUG) alert("ERROR: Attempting to create Horizontal Overlay without specifying direction."); return; } this.parent(options, elmt); }, /** * Positions the container. */ position: function() { if (this.options.onRight) this.positionRight(); else this.positionLeft(); this.positionShim(); }, adjustTop: function(posTop, containerCoords){ var t = posTop; // adjust if inside a scolling element if (this.source.hasClass('overflown')){ var scrollable = this.source.getParent('.scrollable'); if (scrollable) t = t - scrollable.getSize().scroll.y; } // adjust if too near bottom edge var bottomEdge = window.getScrollTop() + window.getHeight(); if (t + containerCoords.height >= bottomEdge){ t = bottomEdge - containerCoords.height - 1; } return t; }, /** * Positions the container to the left of the source element. */ positionLeft: function() { var s = this.source.getCoordinates(); var mT = this.container.getStyle('margin-top').toInt(); var pageSize = $('PAGE').getCoordinates(); var c = this.container.getCoordinates(); // too close to left edge of #PAGE? if (!this.flipped && s.left - pageSize.left - (this.cachedWidth || c.width) < 0) { this.flipped = true; this.options.onRight = true; this.positionRight(); return; } var posTop = this.adjustTop(s.top, c); // final position this.container.setStyles({ left: null, right: window.getWidth() - s.left, top: posTop - mT }); }, /** * Positions the container to the right of the source element. */ positionRight: function() { var s = this.source.getCoordinates(); var mT = this.container.getStyle('margin-top').toInt(); var pageSize = $('PAGE').getCoordinates(); var c = this.container.getCoordinates(); // too close to right edge of #PAGE? if (!this.flipped && pageSize.right - s.right - (this.cachedWidth || c.width) < 0) { this.flipped = true; this.options.onRight = false; this.positionLeft(); return; } var posTop = this.adjustTop(s.top, c); // final position this.container.setStyles({ right: null, left: s.right, top: posTop - mT }); } }); /** * Convenience class for creating an overlay to the right. * @class */ ta.overlays.RelativeOverlayRight = ta.overlays.RelativeOverlayHorizontal.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { this.parent($merge({onRight:true}, options), elmt); } }); /** * Convenience class for creating an overlay to the left. * @class */ ta.overlays.RelativeOverlayLeft = ta.overlays.RelativeOverlayHorizontal.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { this.parent($merge({onRight:false}, options), elmt); } }); /** * Flyout below the source element. * @class * */ ta.overlays.RelativeOverlayVertical = ta.overlays.RelativeOverlay.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { if (!$defined(options.below)) { if (IS_DEBUG) alert("ERROR: Attempting to create Vertical Overlay without specifying direction."); return; } this.parent(options, elmt); }, /** * Positions the container. */ position: function() { if (this.options.below) this.positionBelow(); else this.positionAbove(); this.positionShim(); }, /** * Positions the container. */ positionBelow: function() { var s = this.source.getCoordinates(); var c = this.container.getCoordinates(); var mL = this.container.getStyle('margin-left').toInt(); var bottomEdge = window.getHeight() + window.getScrollTop(); var posTop = s.bottom; // only flip above if flyout will fit if (!this.flipped && bottomEdge - c.height - posTop < 0 && s.top - c.height > 0) { this.options.below = false; this.flipped = true; this.positionAbove(); return; } this.container.setStyles({ left: this.adjustEdge(s.left - mL, c.width), top: posTop }); }, /** * Positions the container. */ positionAbove: function() { var s = this.source.getCoordinates(); var c = this.container.getCoordinates(); var mL = this.container.getStyle('margin-left').toInt(); var topEdge = window.getScrollTop(); var posTop = s.top - c.height; if (!this.flipped && posTop - topEdge < 0) { this.options.below = true; this.flipped = true; this.positionBelow(); return; } this.container.setStyles({ left: this.adjustEdge(s.left - mL, c.width), top: posTop }); }, /** * Adjusts the edge number so that the container is fully within the PAGE bounds. * @param edge the left edge * @param width the container's width * @returns {integer} the adjusted left edge */ adjustEdge: function(edge, width){ var pageSize = $('PAGE').getCoordinates(); if (edge + width >= pageSize.right){ var mR = this.container.getStyle('margin-right').toInt(); edge = pageSize.right - width - mR - 1; } return edge; }, /** * Removes the container from the DOM. * @param {Event} evnt The event */ hideNow: function(evnt) { this.parent(evnt); this.flipped = false; } }); /** * Convenience class for creating an overlay below. * @class */ ta.overlays.RelativeOverlayBelow = ta.overlays.RelativeOverlayVertical.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { this.parent($merge({below:true}, options), elmt); } }); /** * Convenience class for creating an overlay above. * @class */ ta.overlays.RelativeOverlayAbove = ta.overlays.RelativeOverlayVertical.extend({ /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(options, elmt) { this.parent($merge({below:false}, options), elmt); } }); (function() { var _findRemoteURI = function(elmt) { if (elmt.getTag() == 'a') return elmt.href; var tmp = elmt.getElement('a'); if (tmp) return tmp.href; tmp = elmt.getElement('.overlaySrc'); if (tmp) return tmp.getText(); return null; } /** * Overlay factory. * @class */ ta.overlays.Factory = { /** * Creates an overlay in the center of the screen, with a backdrop, loading the content from the * href of the element, or the href of the first A child element, or the contents of the * overlaySrc element. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relRightRemoteHLB: function(evt, elmt) { elmt = $(elmt); if(elmt.getTag() != 'div') { elmt = elmt.getParent(); } var uri = _findRemoteURI(elmt); if (uri == null) return; var overlay = new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_HOVER, pinnable: ta.overlays.PINNABLE_CLICK, backdrop: ta.overlays.BACKDROP_PINNED, style: 'commerceOverlay', showCloseButton: true }, elmt); }, relRightRemoteHLB_Commerce: function(evnt, elmt) { var e = new Event(evnt || window.event); e.preventDefault(); elmt = $(elmt); var uri = _findRemoteURI(elmt); if (uri == null) return; elmt.onmouseover = null; new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_CLICK, pinnable: ta.overlays.PINNABLE_ALWAYS, backdrop: ta.overlays.BACKDROP_ALWAYS, style: 'commerceOverlay', showCloseButton: true }, elmt); }, relRightRemoteHLB_CommCopy: function(evnt, elmt) { var e = new Event(evnt || window.event); e.preventDefault(); elmt = $(elmt); var uri = _findRemoteURI(elmt); if (uri == null) return; elmt.onmouseover = null; new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_HOVER, pinnable: ta.overlays.PINNABLE_CLICK, backdrop: ta.overlays.BACKDROP_PINNED, style: 'commerceOverlay', showCloseButton: true, onLoad: ta.commerce.CROverlay.commCopyUpdate }, elmt); }, relBelowRemoteHLB_Commerce: function(evnt, elmt){ elmt = $(elmt); new Event(evnt || window.event).preventDefault(); var uri = _findRemoteURI(elmt); if (uri == null) return; elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_CLICK, pinnable: ta.overlays.PINNABLE_ALWAYS, backdrop: ta.overlays.BACKDROP_ALWAYS, style: 'commerceOverlay', showCloseButton: true }, elmt); }, relBelowRemoteHLB_CommCopy: function(evnt, elmt){ elmt = $(elmt); new Event(evnt || window.event).preventDefault(); var uri = _findRemoteURI(elmt); if (uri == null) return; elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_CLICK, pinnable: ta.overlays.PINNABLE_ALWAYS, backdrop: ta.overlays.BACKDROP_ALWAYS, style: 'commerceOverlay', showCloseButton: true, onLoad: ta.commerce.CROverlay.commCopyUpdate }, elmt); }, /** * Creates a default overlay below the element. Should be called on click. Note that this * handler stops the event. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relBelow: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayBelow({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK }, elmt); }, /** * Creates a default overlay above the element. Should be called on click. Note that this * handler stops the event. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relAbove: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayAbove({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK }, elmt); }, /** * Creates a default overlay to the right. Should be called onclick. Note that this * handler stops the event. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relRight: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayRight({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK }, elmt); }, /** * Creates a default overlay to the left. Should be called onclick. Note that this * handler stops the event. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relLeft: function(evnt, elmt) { evnt.stop(); elmt.onclick = null; new ta.overlays.RelativeOverlayLeft({ showCloseButton: true, activate: ta.overlays.ACTIVATE_CLICK }, elmt); }, /** * Creates a default overlay below the element. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relBelowH: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_HOVER }, elmt); }, /** * Creates a mediaBox overlay below the element. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relBelowH_Media: function(evnt, elmt) { evnt.stop(); elmt.onmouseover = null; new ta.overlays.RelativeOverlayBelow({ activate: ta.overlays.ACTIVATE_HOVER, style: 'typeO mediaBox' }, elmt); }, /** * Creates a default overlay above the element. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relAboveH: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayAbove({ activate: ta.overlays.ACTIVATE_HOVER }, elmt); }, /** * Creates a default overlay to the right. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relRightH: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_HOVER }, elmt); }, /** * Creates a default overlay to the left. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ relLeftH: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayLeft({ activate: ta.overlays.ACTIVATE_HOVER }, elmt); }, /** * Creates a typeB style overlay to the right. Should be called onmouseover. * * @param {Event} evnt The Event * @param {Element} elmt The element */ typeB_relRightH: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_HOVER, style: 'commerce' }, elmt); }, /** * Creates a typeB style overlay to the right. Should be called onmouseover. Converts to pinned * with backdrop on click. * * @param {Event} evnt The Event * @param {Element} elmt The element */ typeB_relRightHLB: function(evnt, elmt) { elmt.onmouseover = null; new ta.overlays.RelativeOverlayRight({ activate: ta.overlays.ACTIVATE_HOVER, pinnable: ta.overlays.PINNABLE_CLICK, backdrop: ta.overlays.BACKDROP_PINNED, style: 'commerce', showCloseButton: true }, elmt); }, /** * Creates an overlay in the center of the screen, with a backdrop, loading the content from the * href of the element, or the href of the first A child element, or the contents of the * overlaySrc element. * * @param {Event} evnt The Event * @param {Element} elmt The element */ centeredRemoteLB: function(evnt, elmt) { var uri = _findRemoteURI(elmt); if (uri == null) return; evnt.preventDefault(); var overlay = new ta.overlays.CenteredOverlay({ backdrop: ta.overlays.BACKDROP_ALWAYS }); overlay.loadRemoteContent(uri); }, /** * Creates an overlay in the top left corner of the screen, loading the content from the * href of the element, or the href of the first A child element, or the contents of the * overlaySrc element. */ absRemote: function(evnt, elmt) { var uri = _findRemoteURI(elmt); if (uri == null) return; evnt.preventDefault(); var overlay = new ta.overlays.AbsoluteOverlay(); overlay.loadRemoteContent(uri); } } })(); /** * An overlay to show the calendar widget. * @class * * @option {boolean} [showCloseButton=true] show the close button * @option {integer} [activate=ta.overlays.ACTIVATE_FOCUS] method of overlay activation * @option {String} [style=typeO cal] overlay style class * @option {boolean} [autoShow=false] false to prevent overlay from being shown before calendar is ready * @option {boolean} [autoShowCal=false] show calendar on creation * @option {Object} [calendarOptions] hash of options to use when creating the ta.widgets.Calendar */ ta.overlays.CalendarOverlay = ta.overlays.RelativeOverlayBelow.extend({ options: { showCloseButton: true, activate: ta.overlays.ACTIVATE_FOCUS, style: 'typeO ocal', autoShow: false, autoShowCal: false, calendarOptions: null }, /** * @param {Object} options Options, see below. * @param {Element} elmt Element the triggers the flyout. */ initialize: function(container, options){ this.input = container.getElement('input[type=text]'); this.parent(options, this.input); this.input.overlay = this; this.dayField = container.getElement('input.day'); this.monthField = container.getElement('input.month'); this.source.addEvent('click', this.showHandler); this.icn = container.getElement('span.icn'); if (this.icn){ this.icn.onclick = ""; this.icn.addEvent('click', this.showHandler); } this.calendar = new ta.widgets.Calendar($merge({ dualCalendar: true, choiceLink: 'ta.overlays.calendarSelect', prevAction: 'ta.overlays.calendarPrev', nextAction: 'ta.overlays.calendarNext', onSelect: this.select.bind(this) }, this.options.calendarOptions)); this.inner.adopt(this.calendar.container); this.parentOverlay = container.getParent('.overlay'); if (this.parentOverlay){ this.parentOverlay = this.parentOverlay.overlay; this.options.isChild = true; } if (this.options.autoShowCal) this.show(); }, /** * Removes any links between js and DOM to prevent memory leaks. */ destroy: function(){ this.parent(); this.input.overlay = null; }, /** * Adds the container to the DOM and positions it. * @param {Event} evnt The event */ show: function(e){ var vis = this.visible; this.parent(e); if (vis) return; // pin the parent overlay if it is pinnable and isn't already if (this.parentOverlay && this.parentOverlay.pin && this.parentOverlay.options.pinnable == ta.overlays.PINNABLE_CLICK){ this.tempPin = !this.parentOverlay.pinned; if (this.tempPin) this.parentOverlay.pin(); } this.loadFields(); this.calendar.update(); this.calendar.reposition(); return this; }, hide: function(e){ if (!this.visible) return; this.parent(e); // unpin the parent overlay if it was pinned because this child was shown if (this.parentOverlay && this.tempPin){ this.tempPin = false; this.parentOverlay.unpin(); } }, hideNow: function(e){ if (!this.visible) return this; this.parent(e); // unpin the parent overlay if it was pinned because this child was shown if (this.parentOverlay && this.tempPin){ this.tempPin = false; this.parentOverlay.unpin(); } }, /** * Called when the user selects a date in a calendar. * @param {ta.widgets.Calendar} calendar calendar the user selected a date in * @param {Event} evnt the event */ select: function(calendar, evnt){ new Event(evnt).preventDefault(); this.updateFields(); this.hide(); this.fireEvent('onSelect', this); }, /** * Sets the selected date from the values in the fields. */ loadFields: function(){ var d = parseInt(this.dayField.value.replace(/^0/,'')); var m = this.monthField.value.split(/\//); if (m.length != 2) return; var y = parseInt(m[1]); m = parseInt(m[0].replace(/^0/,'')); this.calendar.selectedDate = new Date(y,m-1,d); return this; }, /** * Updates the fields with the new selected date. */ updateFields: function(){ if (!this.calendar.selectedDate) this.loadFields(); if (!this.calendar.selectedDate) return; var m = this.calendar.selectedDate.getMonth() + 1; if (m < 10) m = "0" + m; this.dayField.value = this.calendar.selectedDate.getDate(); this.monthField.value = m + "/" + this.calendar.selectedDate.getFullYear(); this.input.value = formatDate(this.calendar.selectedDate.getDate(), this.calendar.selectedDate.getMonth(), this.calendar.selectedDate.getFullYear()); }, /** * Clears the input field, showing instead the date format. */ clearFields: function(){ this.input.value = JS_DateFormat; }, /** * Verifies the range and format of the fields backing this calendar. * @returns {String} error message, or null if no error */ validate: function(){ var r = this.input.value.match(DATE_FORMAT.pattern); if (r) { var nd = parseInt(r[DATE_FORMAT.date].replace(/^0/,'')); var nm = parseInt(r[DATE_FORMAT.month].replace(/^0/,'')); var ny = parseInt(r[DATE_FORMAT.year].replace(/^0/,'')); //if (d != nd || m != nm || y != ny) { if (nd > 0 && nd < 32 && nm > 0 && nm < 13) { if (nd < 10) nd = '0' + nd; if (nm < 10) nm = '0' + nm; if (ny < 100) ny += 2000; this.dayField.value = nd; this.monthField.value = nm + "/" + ny; this.loadFields(); if (!this.calendar.isValid()) return sInvalidDates; // date out of range } else { return sInvalidDates; } // number out of range } else { return sInvalidDates; } // invalid format return null; } }); ta.overlays.createCalendar = function(evnt, elmt){ new Event(evnt).preventDefault(); elmt.onfocus = ""; new ta.overlays.CalendarOverlay($(elmt).getParent('.cal'), {autoShowCal: true}); } // These functions are for performance. They allow us to construct the html for the calendar using // strings and then hook back into the calendar instance to call the functions as normal. /** * Click handler for dates in the calendar. * @param {Event} evnt the event * @param {integer} y the year * @param {integer} m the month (0 indexed) * @param {integer} d the day of the month * @param {Element} elmt element the user clicked on */ ta.overlays.calendarSelect = function(evnt, y, m, d, elmt){ $(elmt).getParent('.overlay').overlay.calendar.select(evnt, new Date(y, m, d)); } /** * Click handler for the previous month button. * @param {Event} evnt the event * @param {Element} elmt the button */ ta.overlays.calendarPrev = function(evnt, elmt){ $(elmt).getParent('.overlay').overlay.calendar.prev(); } /** * Click handler for the next month button. * @param {Event} evnt the event * @param {Element} elmt the button */ ta.overlays.calendarNext = function(evnt, elmt){ $(elmt).getParent('.overlay').overlay.calendar.next(); } /** * Creates a pair of calendars where the date in the first cannot come after the date in the second, * or vice-versa. * @class * * @option {boolean} [validate=false] validate calendars before form submission * @option {boolean} [allowSameDay=false] allow same day to be selected on both calendars * @option {Object} [calendarOptions] hash of options to use when creating the ta.widgets.Calendar */ ta.overlays.PairedCalendar = new Class({ options: { validate: false, allowSameDay: false, calendarOptions: null }, /** * @param {Element} elmt any child element of .dualCal * @param {Object} [options] options, see below */ initialize: function(elmt, options){ this.setOptions(options); elmt = $(elmt); this.form = elmt.getTag() == 'form' ? elmt : elmt.getParent('form'); var overlayOptions = $merge({onSelect: this.adjustRange.bind(this)}, this.options.overlayOptions); if (this.options.calendarOptions) overlayOptions.calendarOptions = this.options.calendarOptions; this.form.getElements('.cal').each(function(container){ var cal = new ta.overlays.CalendarOverlay(container, overlayOptions); cal.input.onfocus = ""; if (container.hasClass('first')) this.before = cal; else this.after = cal; if (elmt == cal.input || elmt == cal.icn) cal.show(); }, this); if (this.form){ this.form.calendar = this; // add submit event to the form to validate dates if (this.options.validate){ this.form.addEvent('submit', this.validate.bindWithEvent(this)); this.errorDiv = this.form.getElement('.error_msg'); } } }, /** * Finds the corresponding overlay given it's calendar. * @param {ta.widgets.Calendar} calendar the calendar * @returns {ta.overlays.CalendarOverlay} the overlay */ getOverlayForCalendar: function(calendar){ if (this.before.calendar == calendar) return this.before; if (this.after.calendar == calendar) return this.after; return null; }, /** * Adjusts the other calendar of the pair if the range is invalid. */ adjustRange: function(overlay){ this.requireFullLoad(); if (this.form.searchAll){ if (this.form.searchAll.type == 'checkbox') this.form.searchAll.checked = false; else if (this.form.searchAll.type == 'hidden') this.form.searchAll.value = 'false'; } // if after is set to first date and same day is not enabled, set before to first date and after to one day later if (!this.sameDayEnabled() && this.after.calendar.selectedDate && this.after.calendar.selectedDate.getTime() == this.after.calendar.options.firstDate.getTime()){ this.before.calendar.selectedDate = new Date(this.before.calendar.options.firstDate); var d = new Date(this.after.calendar.options.firstDate); d.setDate(d.getDate()+1); this.after.calendar.selectedDate = d; this.before.updateFields(); this.after.updateFields(); } else { if (overlay == this.before){ if (!this.isBeforeValid()){ var d = new Date(this.before.calendar.selectedDate); d.setDate(d.getDate() + 1); this.after.calendar.selectedDate = d; } this.after.updateFields(); } else if (overlay == this.after){ if (!this.isAfterValid()){ var d = new Date(this.after.calendar.selectedDate); d.setDate(d.getDate() - 1); this.before.calendar.selectedDate = d; } this.before.updateFields(); } } this.fireEvent('onUpdate', this); var callbacks = ta.retrieve('calendar.onUpdateCallback'); if (callbacks){ for (var i=0;i this.before.calendar.selectedDate) || (this.sameDayEnabled() && this.after.calendar.selectedDate.getTime() >= this.before.calendar.selectedDate.getTime()); }, /** * Validate that the calendars are correct. This is where user input is checked. */ validate: function(e){ if (this.searchAllEnabled()) return true; var eb = this.before.validate(); var ea = this.after.validate(); var valid = true; if (this.isOneWay()) { if (eb != null) { valid = false; this.setError(eb); } } else if (eb != null || ea != null) { valid = false; this.setError(eb || ea); } else if (!this.isAfterValid()) { valid = false; this.setError(sInvalidDates); } if (!valid && e) new Event(e).stop(); return valid; }, /** * Sets and shows the error message. * @param {String} msg error message to show */ setError: function(msg){ if (!this.errorDiv) return; this.errorDiv.setContent(msg).show(); }, /** * Toggles the search all option */ toggleSearchAll: function(){ if (this.searchAllEnabled()){ this.before.clearFields(); this.after.clearFields(); } else { this.requireFullLoad(); this.before.updateFields(); this.after.updateFields(); } } }); ta.overlays.PairedCalendar.implement(new Options, new Events); /** * Factory method for creating calendars. Should be called onclick. */ ta.overlays.createSimplePairedCalendar = function(evnt, elmt){ new Event(evnt).stop(); new ta.overlays.PairedCalendar(elmt); } /** * Factory method for creating calendars. Should be called onclick. */ ta.overlays.createPairedCalendar = function(evnt, elmt, options){ if(!options) { options = {validate: true}; } new Event(evnt).preventDefault(); new ta.overlays.PairedCalendar(elmt, options); } /** * create calendars and switch to display dates */ ta.overlays.createDatedPairedCalendar = function(evnt, elmt){ new Event(evnt).preventDefault(); elmt = $(elmt); var form = elmt.getParent('form'); new ta.overlays.PairedCalendar(elmt, {validate:true, overlayOptions:{onShow: function(){ ta.overlays.toggleOffSearchAll(form); }}}); //needs an extra call for first time ta.overlays.toggleOffSearchAll(form); } ta.overlays.toggleOffSearchAll = function(form){ if(form.calendar && form.searchAll){ if (form.searchAll.type == 'checkbox') form.searchAll.checked = false; else if (form.searchAll.type == 'hidden') form.searchAll.value = 'false'; form.calendar.toggleSearchAll(); } } 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){ elmt = $(elmt); elmt.onclick = ""; var form = elmt.getParent('form'); if (!form.calendar) new ta.overlays.PairedCalendar(elmt, {validate:true}); form.calendar.toggleSearchAll(elmt); elmt.addEvent('click', form.calendar.toggleSearchAll.bind(form.calendar)); } /** * A permanent overlay positioned absolutely in the window. */ ta.overlays.PermanentOverlay = ta.overlays.AbsoluteOverlay.extend({ }); /** * Observer - Observe formelements for changes * * @version 1.0rc1 * * @license MIT-style license * @author Harald Kirschner * @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 * @copyright Author */ var Autocompleter = {}; Autocompleter.Base = new Class({ options: { minLength: 1, useSelection: true, markQuery: true, inheritWidth: true, maxChoices: 10, injectChoice: null, onSelect: Class.empty, onShow: Class.empty, onHide: Class.empty, customTarget: null, className: 'autocompleter-choices', zIndex: 42, observerOptions: {}, fxOptions: {}, overflown: [], selectOnBlur: false }, initialize: function(el, options) { this.setOptions(options); this.element = $(el); this.build(); this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({ delay: 100 }, this.options.observerOptions)); this.value = this.observer.value; this.queryValue = null; }, /** * build - Initialize DOM * * Builds the html structure for choices and appends the events to the element. * Override this function to modify the html generation. */ build: function() { if ($(this.options.customTarget)) this.choices = this.options.customTarget; else { this.choices = new Element('ul', { 'class': this.options.className, styles: {zIndex: this.options.zIndex} }).injectInside(document.body); this.fix = new OverlayFix(this.choices); } this.fx = this.choices.effect('opacity', $merge({ wait: false, duration: 200 }, this.options.fxOptions)) .addEvent('onStart', function() { if (this.fx.now) return; this.choices.setStyle('display', ''); this.fix.show(); }.bind(this)) .addEvent('onComplete', function() { if (this.fx.now) return; this.choices.setStyle('display', 'none'); this.fix.hide(); }.bind(this)).set(0) .addEvent('onShow', function() { this.fix.show(); }.bind(this)); this.element.setProperty('autocomplete', 'off') .addEvent(window.ie6 ? 'keydown' : 'keypress', this.onCommand.bindWithEvent(this)) .addEvent('mousedown', this.onCommand.bindWithEvent(this, [true])) .addEvent('focus', this.toggleFocus.bind(this, [true])) .addEvent('blur', this.toggleFocus.bind(this, [false])) .addEvent('trash', this.destroy.bind(this)); }, destroy: function() { this.choices.remove(); }, toggleFocus: function(state) { if (!state && this.focussed && this.options.selectOnBlur && this.selected && this.visible) { // losing focus, accept current selection this.choiceSelect(this.selected); } this.focussed = state; if (!state) this.hideChoices(); }, onCommand: function(e, mouse) { if (mouse && this.focussed) this.prefetch(); if (e.key && !e.shift) switch (e.key) { case 'enter': if (this.selected && this.visible) { this.choiceSelect(this.selected); e.stop(); } return; case 'up': case 'down': if (this.observer.value != (this.value || this.queryValue)) this.prefetch(); else if (this.queryValue === null) break; else if (!this.visible) this.showChoices(); else { this.choiceOver((e.key == 'up') ? this.selected.getPrevious() || this.choices.getLast() : this.selected.getNext() || this.choices.getFirst() ); this.setSelection(); } e.stop(); return; case 'esc': this.hideChoices(); return; } this.value = false; }, setSelection: function() { if (!this.options.useSelection) return; var startLength = this.queryValue.length; if (this.element.value.indexOf(this.queryValue) != 0) return; var insert = this.selected.inputValue.substr(startLength); if (document.getSelection) { this.element.value = this.queryValue + insert; this.element.selectionStart = startLength; this.element.selectionEnd = this.element.value.length; } else if (document.selection) { var sel = document.selection.createRange(); sel.text = insert; sel.move("character", - insert.length); sel.findText(insert); sel.select(); } this.value = this.observer.value = this.element.value; }, hideChoices: function() { if (!this.visible) return; this.visible = this.value = false; this.observer.clear(); this.fx.start(0); this.fireEvent('onHide', [this.element, this.choices]); }, showChoices: function() { if (this.visible || !this.choices.getFirst()) return; this.visible = true; var pos = this.element.getCoordinates(this.options.overflown); this.choices.setStyles({ left: pos.left, top: pos.bottom }); if (this.options.inheritWidth) this.choices.setStyle('width', pos.width); this.fx.start(1); this.choiceOver(this.choices.getFirst()); this.fireEvent('onShow', [this.element, this.choices]); }, prefetch: function() { if (this.element.value.length < this.options.minLength) this.hideChoices(); else if (this.element.value == this.queryValue) this.showChoices(); else this.query(); }, updateChoices: function(choices) { this.choices.empty(); this.selected = null; if (!choices || !choices.length) return; if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices; choices.each(this.options.injectChoice || function(choice, i){ var el = new Element('li').setHTML(this.markQueryValue(choice)); el.inputValue = choice; this.addChoiceEvents(el).injectInside(this.choices); }, this); this.showChoices(); }, choiceOver: function(el) { if (this.selected) this.selected.removeClass('autocompleter-selected'); this.selected = el.addClass('autocompleter-selected'); }, choiceSelect: function(el) { this.observer.value = this.element.value = el.inputValue; this.hideChoices(); this.fireEvent('onSelect', [this.element], 20); }, /** * markQueryValue * * Marks the queried word in the given string with * * 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'), '$1') : 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 , , , , License: not free for public use */ /* Class: StringBuffer Small implementation of a string buffer. */ var StringBuffer = new Class({ initialize: function(str) { this.strings = new Array(); if (str) this.append(str); }, append: function(str) { this.strings.push(str); return this; }, toString: function() { return this.strings.join(''); }, isEmpty: function () { return this.strings.length == 0; } }); Cookie.extend({ set: function(key, value, options){ Cookie.setRaw(key, encodeURIComponent(value), options); }, setRaw: function(key, value, options){ options = $merge(this.options, options); if (options.domain) value += '; domain=' + options.domain; if (options.path) value += '; path=' + options.path; if (options.duration){ var date = new Date(); date.setTime(date.getTime() + options.duration * 24 * 60 * 60 * 1000); value += '; expires=' + date.toGMTString(); } if (options.time){ var date = new Date(); date.setTime(date.getTime() + options.time * 1000); value += '; expires=' + date.toGMTString(); } if (options.secure) value += '; secure'; document.cookie = key + '=' + value; return $extend(options, {'key': key, 'value': value}); }, exist: function(key){ return document.cookie.match('(?:^|;)\\s*' + key.escapeRegExp() + '=([^;]*)') != null; }, // Like Cookie.get, but doesn't try to decodeURIComponent (fails on some of our cookies) getRaw: function(key) { var val = document.cookie.match('(?:^|;)\\s*' + key.escapeRegExp() + '=([^;]*)'); return val ? val[1] : false; } }); Array.extend({ toQueryString: function() { var data = this.map(function(item, idx) { switch($type(item)){ case 'element': return $(item).toQueryString(); break; case 'object': return Object.toQueryString(item); } }); return data.join('&'); } }); Ajax.prototype._initialize = Ajax.prototype.initialize; Ajax.prototype.initialize = function(url, options){ // force URLs to be absolute to fix issue with BASE tag on geo-hostnames if (!/^http/.test(url)) { url = window.location.protocol + '//' + window.location.host + url; } else if (/^(\w+):\/\/([\w.\-]+)\/(.*)/.test(url) && RegExp.$2 != window.location.host) { url = RegExp.$1 + '://' + window.location.host + '/' + RegExp.$3; } // support data as an array if (options && options.data && options.data.constructor && options.data.constructor == Array) { options.data = options.data.toQueryString(); } // continue with normal initialization return this._initialize(url, options); } Element.extend({ getParents: function(selector){ return $$(selector || '').filter(function(el){ return (el.hasChild(this));}, this).reverse(); }, getParent: function(selector){ if (!selector) return $(this.parentNode); return this.getParents(selector)[0] || null; }, /** * this hack function is needed for searching elements * that have been removed from the dom. It searches by * iterating up the parentNode, instead of using * the global $$ function which references the dom */ findParentByClass: function(myClass){ if (!this.parentNode) return null; var pNode = $(this.parentNode); if (pNode == document) return null; if (pNode.hasClass(myClass)) return pNode; return pNode.findParentByClass(myClass); }, getLastElement: function(selector){ return $(this.getElements(selector, true).reverse()[0] || false); }, centerOnScreen: function(){ var s = this.getCoordinates(); this.style.left = Math.round((Window.getWidth() - s.width) / 2) + Window.getScrollLeft() + 'px'; this.style.top = Math.round((Window.getHeight() - s.height) / 2) + Window.getScrollTop() + 'px'; }, positionOnScreen: function(left, top){ this.style.left = Window.getScrollLeft() + left + 'px'; this.style.top = Window.getScrollTop() + top + 'px'; }, contains: function(evnt){ if (!evnt || !evnt.page) return false; evnt = new Event(evnt); var s = this.getCoordinates(); return (evnt.page.y >= s.top && evnt.page.y <= s.bottom && evnt.page.x >= s.left && evnt.page.x <= s.right); }, overlaps: function(elmt){ var c = this.getCoordinates(); var e = elmt.getCoordinates(); var inH = (e.left > c.left && e.left < c.right) || (e.right > c.left && e.right < c.right); var top = e.top > c.top && e.top < c.bottom; var btm = e.bottom > c.top && e.bottom < c.bottom; return (top && inH) || (btm && inH); }, // linkify caries the class from the element being replaced forward to the new 'a' tag linkify: function(){ var a = this.replaceWith(new Element('a', {'class': this.className, href: 'javascript:;'})).setHTML(this.innerHTML); _processLink(a); return a; }, setContent: function(content) { if (content instanceof Array) content.each( function(v) { this.adopt($(v)); }, this ); // IDs are case insensitive in IE // don't get an element by id, just check if its already an element else if ($type(content) == 'element') this.adopt($(content)); else this.setHTML(content); return this; }, hidden: function() { return this.getStyle('display') == 'none'; }, show: function() { return this.setStyle('display', 'block'); }, hide: function() { return this.setStyle('display', 'none'); }, toggle: function() { if (this.hidden()) this.show(); else this.hide(); }, inDocument: function() { return this.parentNode != null && this.parentNode.nodeType != 11; } }); /* Class: Table Options: properties - (object) properties to set on the table rows - (array) table contents */ // TODO should this extend element with overrides so it can be injected and so forth? var Table = new Class({ options: { properties: { cellpadding: 0, cellspacing: 0, border: 0 }, rows: [] }, initialize: function(options) { this.setOptions(options); this.table = new Element('table').setProperties(this.options.properties); this.tbody = new Element('tbody').injectInside(this.table); this.options.rows.each(this.push.bind(this)); }, /* Property: push Add contents to a section of the table. Arguments: row - (array) content to be added. Each item is a cell. section - thead or tfoot, defaults to tbody head - (boolean) true to use th instead of td, default is false */ push: function(row, section, head) { var tr = new Element('tr').injectInside(section || this.tbody); row.each(function (v) { var td = new Element(head ? 'th' : 'td').injectInside(tr); if (v.properties) td.setProperties(v.properties); if (v.content) td.setContent(v.content); else td.setContent(v); }); return this; }, /* Property: pushHead Add a row of content to the thead. Uses TRs instead of TDs. Arguments: row - (array) content to be added. Each item is a cell. */ pushHead: function(row) { if (!this.thead) this.thead = new Element('thead').injectTop(this.table); this.push(row, this.thead, true); return this; }, /* Property: pushFoot Add a row of content to the tfoot. Arguments: row - (array) content to be added. Each item is a cell. */ pushFoot: function(row) { if (!this.tfoot) this.tfoot = new Element('tfoot').injectInside(this.table); this.push(row, this.tfoot); return this; }, /* Property: pushCaption */ // TODO should be set caption and only allow one pushCaption: function(content) { new Element('caption').setContent(content).injectTop(this.table); return this; } }); Table.implement(new Options); /* Class: Autocompleter.Ajax.Json2 Modified version of Autocompleter.Ajax.Json that uses objects for the response instead of a string array. */ Autocompleter.Ajax.Json2 = Autocompleter.Ajax.Json.extend({ updateChoices: function(choices) { this.choices.empty(); this.selected = null; if (!choices || !choices.length) return; if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices; choices.each(this.options.injectChoice || function(choice, i){ var el = new Element('li').setHTML(this.markQueryValue(choice.name)); el.responseObj = choice; el.inputValue = choice.name; this.addChoiceEvents(el).injectInside(this.choices); }, this); this.showChoices(); }, choiceSelect: function(el) { if (el.responseObj.notFound) { this.hideChoices(); return; } this.observer.value = this.element.value = el.inputValue; this.hideChoices(); this.fireEvent('onSelect', [this.element, el.responseObj], 20); }, queryResponse: function(resp) { // if the input does not have focus any more then just return if (!this.focussed) { return; } this.value = this.queryValue = this.element.value; this.selected = false; this.hideChoices(); this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20); var choices = Json.evaluate(resp || false); if (!choices || !choices.length) { choices = [{ name : JS_location_not_found, value: JS_location_not_found, notFound: true}]; } this.updateChoices(choices); } }); // subclass the autocomplete to make it more closely match noreaster behavior Autocompleter.Ajax.Flights = Autocompleter.Ajax.Json2.extend( { initialize: function(el, url, options) { this.parent(el, url, options); this.verifyAjax = new Ajax(url, {autoCancel: true, method: 'get'}); this.verifyAjax.addEvent('onComplete', this.verifyResponse.bind(this)); this.verifyAjax.addEvent('onFailure', this.verifyResponse.bind(this, [false])); }, // override escape key so that it doesn't hide the list onCommand: function(e, mouse) { if (e.key && !e.shift && e.key == 'esc') { if (mouse && this.focussed) { this.prefetch(); } e.stop(); return; } else { this.parent(e, mouse); } }, // make sure that we capture text in the hidden field toggleFocus: function(state) { this.parent(state); if (!state) { if (this.element.value.length) { // verify that we have a legal airport var data = $extend({}, this.options.postData); data[this.options.postVar] = this.element.value; this.verifyAjax.request(data); } else { // make sure to clear all fields this.fireEvent('onSelect', [this.element, {name:'',value:''}], false); } } }, verifyResponse: function(resp) { var choices = Json.evaluate(resp || false); if (choices && choices.length) { this.fireEvent('onSelect', [this.element, choices[0]], false); this.element.fireEvent('airportupdate'); } else { this.fireEvent('onSelect', [this.element, {name:'',value:''}], false); } } }); /* Class: TabSet The TabSet class creates a group of elements that are toggled when their handles are clicked. When one element toggles in, the others toggle back. Arguments: togglers - required, a collection of elements, the elements handlers that will be clickable. elements - required, a collection of elements the transitions will be applied to. options - optional, see options below, and 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: */ 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 1) id = id.substring(1); var t = $(id); if (t && t.getTop() != window.getScrollTop()) window.scrollTo(0, t.getTop()); } // show any DHTML popup from the PopupManager.java if (typeof showDHTMLOverlayOnLoad != "undefined") { showDHTMLOverlayOnLoad(); } if (typeof cpu_run != "undefined") cpu_run(); if (/^#save/.test(location.hash)) { // saves post login popup doSavesPostLoginPopup(); } }); // Define the ready event - the point at which to load the behavior. // This may need to be load instead of domready with cerain badly-behaving ads. TAReadyEvent = window.TAReadyEvent || "domready"; // apply the rules when the DOM is ready. window.addEvent(TAReadyEvent, behaviorFunction); var bfCount = 0; function behaviorFunction() { if( typeof performancePingbackInit != 'undefined' ) { performancePingbackInit.behaviorStart = new Number( new Date() ); } if (window.ie && (!$('FOOT') || !$('FOOT').readyState || $('FOOT').readyState != 'complete') && bfCount < 10) { bfCount++; behaviorFunction.delay(30); return; } // hide some elements if (window.hideOnLoad) $A(hideOnLoad).each(function(v){ var x = $(v); if (x) x.hide(); }); // grab focus of the search bar searchFocus(); // Process links - don't use rules[] because it is too slow for links. processLinks(document); // register the rules behavior.register(rules); // If the location hash is set to '#Calculator' and we are on a valid // servlet, open the calculator var main = $('MAIN'); if ((window.location.hash == '#Calculator') && ($('HOMEPAGE') || // Home Page (main && main.hasClass('VacationRentals')) || // /VacationRentals $('HOTEL_LANDER'))) // /Hotels { // this is a bit hacky, but don't allow other popups to open on page load. // it has already been added to an onload queue so we will replace it // with a no-op function. The downside here is that we still have a cookie // set as if they saw the popup but they never do. Based on the usage of // this direct link PM (Theresa) said it is ok. showDHTMLOverlayOnLoad = function(){}; // open the calculator vrCalculator(); } // setup the accordion if need be var tabs = $$('#DEST_HOME #DEST_ACCORDION .window'); if (tabs.length > 0) { var elmts = $$('#DEST_HOME #DEST_ACCORDION .pane'); new TabSet(tabs, elmts, { show: 0, collapsable: true, onActive: function(event, toggler, element) { //if (!toggler.hasClass('first')) new Ajax('/ActionRecord?action='+toggler.id).request(); toggler.addClass('active'); }, onBackground: function(event, toggler, element) { toggler.removeClass('active'); } }); } var sisTabs = $$('#SIS_ACCORDION .window'); if (sisTabs.length > 0){ var sisElmts = $$('#SIS_ACCORDION .pane'); // show the first pane with content var showIndex = 0; for (var i=0; i 160) { element.setStyles({height:160, overflow:'auto'}); } }, onBackground: function(event, toggler, element){ toggler.removeClass('active'); element.removeClass('active'); } }); } if( typeof performancePingbackInit != 'undefined' && performancePingbackInit.enabled ) { performancePingbackInit.behaviorEnd = new Number( new Date() ); var loadTime = performancePingbackInit.behaviorStart - performancePingbackInit.start; var behaviorTime = performancePingbackInit.behaviorEnd - performancePingbackInit.behaviorStart; var uri = '/PerformancePingback'+ '?ppuid='+ performancePingbackInit.uid + '&load='+ loadTime + '&behavior='+ behaviorTime + '&servlet='+ performancePingbackInit.servlet; new Ajax(uri).request(); } // Check for a fresh VS cookie // bug: 29564 gbelote if( typeof freshVSTrackingCookie != 'undefined' && freshVSTrackingCookie ) { // Check to see if VS tracking cookie exists var hasVSCookie = Cookie.getRaw ('v1st'); if (hasVSCookie) { // Fire an AJAX request new Ajax ('/VSCookieRequest').request (); } } } // Setup email replacement. e.g. #eobf( 'dmosales' '.com') -> mailto: rulesN['EOBF'] = function(elmt) { var e = elmt.innerHTML+'@tripadvisor'+elmt.title; elmt.replaceWith(new Element('a', {href: 'mailto:'+e}).setContent(e)); } /* Function: popup Show a poup window. The event target (or one of its ancestors) should be a link. The href of the link will be used as the URL of the popup Arguments: e - the event width - width of the popup height - height of the popup x - horizontal screen offset y - vertical screen offset */ var popupIndex = 0; function popup(e, name, width, height, x, y, noScroll, buildUrl, allOptions) { new Event(e).preventDefault(); var ops = ""; if (sz = this.className.match(/sz(\d+)x(\d+)/)) { width = sz[1]; height = sz[2]; } if (this.href.indexOf("p=HotelsCom") > -1) { width = Math.max(width, 960); height = Math.max(height, window.getHeight(), 600); } if (width) ops += ",width="+width; if (height) ops += ",height="+height; if (x) ops += ",screenX="+x+",left="+x; if (y) ops += ",screenY="+y+",top="+y; if (ops != "") { if (allOptions) { ops = "toolbar=1,resizable=1,menubar=1,location=1,status=1,scrollbars=1" + ops; } else { ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=" + (noScroll ? 0 : 1) + ops; } } var w = window.open(buildUrl ? buildUrl : this.href, name || "p"+window.name+(popupIndex++), ops); if (w != null) { try {w.opener = self;} catch (exc) {} w.focus(); } } // 'this' is the element function formSubmit(e) { new Event(e).preventDefault(); this.click(); } function unobf(uri) { return (uri.substring(0,8) == "NOFOLLOW" ? "http://" + uri.substring(8) : "/" + uri) + ".html"; } /* Function: toggle Toggle the class (default: 'off') of the element this function is bound to. */ function toggle(e) {new Event(e).preventDefault(); this.toggleClass('off');} /* Some generic rules ---------------------------------------------------------------------------------------------- */ // called when a link with a js_ class is clicked. var doBehavior = function(e) { var e = new Event(e || window.event); var elmt = $(e.target); if (elmt.getTag() != "a") elmt = elmt.getParent("a"); if (/pid(\d+)/.test(elmt.className)) Cookie.set('NPID', RegExp.$1, {domain: cookieDomain, time:5}); $pick(elmt.className.match(/(js_\w+)/g), []).each(function(c) { linkMap[c](elmt, e); }); } function _processLink(elmt) { if (elmt.className.indexOf('js_') >= 0 || elmt.className.indexOf('pid') >= 0) { if (elmt.addEventListener) elmt.addEventListener('click', doBehavior, false); else elmt.attachEvent('onclick', doBehavior); } } var setPID = function(pid) { Cookie.set('NPID', pid, {domain: cookieDomain, time:5}) } /* All Link rules in one function for performance */ function processLinks(root) { var aTags = $A(root.getElementsByTagName('a')); aTags.each(_processLink); return aTags.length; } function searchFocus() { if($('mainSearch')) { try { $('mainSearch').focus(); } catch(e) { } } } var linkMap = {}; // submit forms via AJAX linkMap['js_ajaxSubmit'] = function(elmt, e) { var uri = elmt.href; if (!elmt.className.match(/\bform_(\w+)\b/)) return; var form = $(RegExp.$1); var ops = { data: form, onFailure: function(e) { if (form) form.removeClass('ajaxInFlight'); alert(JS_Ajax_failed); } } var cbfn = window['callback'+form.id]; if (elmt.className.match(/\method_(\w+)\b/)) ops.method = RegExp["$1"]; if (elmt.className.match(/\btgt_(\w+)\b/)) ops.update = RegExp["$1"]; else ops.onComplete = function(text, xml) { if (form) form.removeClass('ajaxInFlight'); ta.overlays.showInLightbox(text); if ( $defined(cbfn) && $type(cbfn) == "function" ) { cbfn(); } } new Event(e).preventDefault(); var fn = window['validate'+form.id]; if ( $defined(fn) && $type(fn) == "function" && fn.apply(form) == false ) return; form.addClass('ajaxInFlight'); if (elmt.className.match(/\bevalScripts\b/)) ops.evalScripts = true; new Ajax(uri, ops).request(); }; // used in translations: newsletter_9b5, newsletter_9b6 linkMap['js_ajax'] = function(elmt, e) { new Event(e).preventDefault(); if (!(tgt = elmt.className.match(/\btgt_(\w+)\b/))) return;// target is required tgt = $(tgt[1]); new Ajax(elmt.href, { update: tgt, onFailure: function(e) { alert(JS_Ajax_failed); } }).request(); }; // like 'ajax', but expected to log-in first linkMap['js_ajaxlogin'] = function(elmt, e) { new Event(e).preventDefault(); if (!(tgt = elmt.className.match(/\btgt_(\w+)\b/))) return;// target is required var alHref = elmt.href; tgt = $(tgt[1]); new Ajax(elmt.href, { onComplete: function(txt, xml) { var bWasLoggedIn = (txt.indexOf('')<0); if(!bWasLoggedIn) { login(['tt','ajax','returnTo', alHref + "&rd=1", 'greeting', 'showuserreviews_vote_25ee' ]); } else { tgt.innerHTML = txt; } }, onFailure: function(e) { alert(JS_Ajax_failed); } }).request(); }; linkMap['js_ajaxMsg'] = function(elmt, e) { if (elmt.className.match(/\bcallback_ShowRecaptcha\b/)) { new Asset.javascript('http://api.recaptcha.net/js/recaptcha_ajax.js'); } ajaxMsg.attempt(e, elmt); }; linkMap['js_ajaxReport'] = function(elmt, e) { ajaxReport.attempt(e, elmt); }; // open links in popups of various sizes linkMap['js_popup'] = function(elmt, e) { popup.attempt(e, elmt); }; linkMap['js_popComm'] = function(elmt, e) { if (typeof pageTracker != "undefined") { var utmp = "/"+elmt.href.replace(/^http:\/\/.+\//i, ""); pageTracker._trackPageview(utmp); } popup.attempt([e, null, 950, 610, null, null, null, null, true], elmt); }; // FIXME: tourism-narrow only linkMap['js_popNoScroll'] = function(elmt, e) { popup.attempt([e, null, null, null, null, null, true], elmt); }; linkMap['js_popCR'] = function(elmt, e) { popup.attempt([e, 'cr', 245, 610, 5, 5], elmt); }; linkMap['js_email'] = function(elmt, e) { popup.attempt([e, 'email', 580, 460, 30, 25], elmt); }; // FIXME: SUR only linkMap['js_popReview'] = function(elmt, e) { popup.attempt([e, 'review', 550, 395, 30, 25], elmt); }; linkMap['js_popPhoto'] = function(elmt, e) { popup.attempt([e, 'photo', 650, 350], elmt); }; linkMap['js_popGallery'] = function(elmt, e) { Cookie.set('PhotoPop', '1', {domain: cookieDomain}); popup.attempt([e, 'media', 782, 820], elmt); }; linkMap['js_popGalleryComm'] = function(elmt, e) { Cookie.set('PhotoPop', '1', {domain: cookieDomain}); popup.attempt([e, 'media', 780, 950], elmt); }; linkMap['js_popDMO'] = function(elmt, e) { popup.attempt([e, 'dmo', 400, 400], elmt); }; // FIXME: nexus only linkMap['js_popNxTall'] = function(elmt, e) { popup.attempt([e, 330, 680], elmt); }; linkMap['js_popNxWide'] = function(elmt, e) { popup.attempt([e, 730, 380], elmt); }; linkMap['js_popNxLogin'] = function(elmt, e) { popup.attempt([e, 'login', 795, 645], elmt); }; linkMap['js_popFraud'] = function(elmt, e) { popup.attempt([e, 'fraud', 640, 460], elmt); }; linkMap['js_popDestGd'] = function(elmt, e) { popup.attempt([e, 'dest_guide', 565, 700, (screen.width-700)/2, (screen.height-600)/2], elmt); }; linkMap['js_tamgSubsDestGd'] = function(elmt, e) { window.showTamgSubsLB = function() { new Ajax('/TAMGSubOffers?t=DG_LB', { onComplete: function(txt, xml) { ta.overlays.showInLightbox(txt); }, method: 'get', evalScripts: true }).request(); }; }; linkMap['js_popPromo'] = function(elmt, e) { popup.attempt([e, 'promo', 600, 700, 30, 25], elmt) }; linkMap['js_popSmall'] = function(elmt, e) { if (screen.width > 1024) { popup.attempt([e, null, 800, 600, 240, 5, false, false, true], elmt); } else if (screen.width > 800) { popup.attempt([e, null, 620, 500, 240, 5, false, false, true], elmt); } else { popup.attempt([e, null, 475, 390, 210, 5, false, false, true], elmt); } return elmt; }; linkMap['js_popProfPhoto'] = function(elmt, e) { if (screen.width > 1024) { popup.attempt([e, null, 800, 500, 240, 5, false, false, true], elmt); } else if (screen.width > 800) { popup.attempt([e, null, 620, 450, 240, 5, false, false, true], elmt); } else { popup.attempt([e, null, 475, 390, 210, 5, false, false, true], elmt); } return elmt; }; linkMap['js_popPartner'] = function(elmt, e) { if (screen.width > 1024) { popup.attempt([e, null, 980, 500, 240, 5, false, false, true], elmt); } else if (screen.width > 800) { popup.attempt([e, null, 800, 500, 240, 5, false, false, true], elmt); } else { popup.attempt([e, null, 620, 450, 210, 5, false, false, true], elmt); } return elmt; }; linkMap['js_noTAPD'] = function(elmt, e) { Cookie.remove('TAPD', {domain: cookieDomain}); }; // FIXME: only used on nexus linkMap['js_modifySub'] = function(elmt, e) { modifySub.apply(elmt, [e]); }; // FIXME: only used in compose/send message lightbox layer - THIS DOES NOT WORK W/ LINK ASSUMPTION ajaxRules['a.js_delay'] = function(elmt) { if (!(ms = elmt.className.match(/\b(\d+)ms\b/))) return; ms = parseInt(ms[1]); elmt.fireEvent('click', null, ms); }; // TODO: remove this once the new dhtml popup framework gets out of pool testing ajaxRules['a.figsSurveyLink']=function(elmt, e) { Cookie.set('TAPanelSurveyPopup', '-1', {domain:cookieDomain, path:"/", duration:365}); } rules['a#TERMS'] = function(elmt) { elmt.addEvent('click', popup.bindAsEventListener(elmt, ['terms', 300, 300, 30, 25]));}; // IE6 does not support :hover on elements other than // use .hvrIE6 and style with .hvrIE6.mseOvr if (window.ie6 && window.pageServlet != 'Hotel_Review') { var hvrIE6Fn = function(elmt) { elmt.addEvent('mouseenter', function() { elmt.addClass('mseOvr'); }); elmt.addEvent('mouseleave', function() { elmt.removeClass('mseOvr'); }); } rules['span.hvrIE6'] = hvrIE6Fn; rules['label.hvrIE6'] = hvrIE6Fn; rules['h2.hvrIE6'] = hvrIE6Fn; } // Disabled - For next sponsorship, target using an ID and move to page-specific JS file(s) /*rules['area.js_popSmall'] = function(elmt) { elmt.addEvent('click', function() { if (screen.width > 1024) { popup.attempt([null, 800, 600, 240, 5, false, false, true], elmt); } else if (screen.width > 800) { popup.attempt([null, 620, 500, 240, 5, false, false, true], elmt); } else { popup.attempt([null, 475, 390, 210, 5, false, false, true], elmt); } }); }*/ // used by TW and MU callouts on LHS, TCH2009 // text elements with class 'focusClear' have their value cleared on focus if it is the default value // Sep 09 Added onBlur - restores default if input is empty //rules['#NEWSLETTER_CALLOUT input.focusClear[type=text]'] = rules['#USER_REVIEW_FORM input.focusClear[type=text]'] = rules['#TCH input.focusClear[type=text]'] = rules['#TCH_POP_FRM input.focusClear[type=text]'] = rules['#TCH2009_WIDGET input.focusClear[type=text]'] = rules['#HOMEPAGE input.focusClear[type=text]'] = rules['#homePageVRCalcPromo input.focusClear[type=text]'] = rules['#WHS_MAIN input.focusClear'] = rules['#WHS_POP input.focusClear[type=text]'] =function(elmt) { elmt.addEvent('focus', function() { if (elmt.value == elmt.defaultValue) { elmt.removeClass('focusClear'); elmt.value = ''; } }); elmt.addEvent('blur', function() { if (elmt.value == "") { elmt.addClass('focusClear'); elmt.value = elmt.defaultValue; } }); } rules['#NEWSLETTER_CALLOUT input.memberUpdateEmail[type=text]'] = function(elmt) { $('memberUpdatesEmailDefault').hide(); $(elmt.form).addEvent('submit', function(e) { if(elmt.value == $('memberUpdatesEmailDefault').innerHTML) { elmt.value = ""; } }); } // don't try to gain focus of search box on any forum servlets /* bug 28219 gbelote rules['#SEARCH_LHN'] = function(elmt) { if(pageServlet != 'ShowForum' && pageServlet != 'ShowTopic' && pageServlet != 'ForumHome' && pageServlet != 'ListForums' && pageServlet != 'NewTopic' && pageServlet != 'PostReply') { window.addEvent('load', function() { try { elmt.getElement('input[type=text]').focus(); } catch(e) { } }); } elmt.getElement('form').addEvent('submit', function(e) {if (elmt.value == '') (new Event(e)).stop();}); } */ // text elements with class 'alertIfEmpty' cause form submission to fail and an alert to be shown if the value is empty // not targetted better because form can occur twice on BYG var alertIfEmpty = function(elmt) { var form = $(elmt.form); if (form) form.addEvent('submit', function(e) { if (elmt.value == '') { if(msg = elmt.className.match(/\bmsg_([\w\d]+)\b/)) { alert(window[msg[1]]); } else { alert('The field can not be empty'); // input fields should instead use a localization key } (new Event(e)).stop(); }}); } rules['#NX_NEW_TITLE input, #NX_NEW_TITLE2 input'] = alertIfEmpty; rules['select.submitOnChange'] = function(elmt) { elmt.addEvent('change', function() {elmt.form.submit();}); } // MOVE - only used on Fun Stuff landing page // rules['select.redirOnChange'] = function(elmt) { // elmt.addEvent('change', openValueInNew.bindAsEventListener(elmt)); // } // clicking the 'search within' checkbox should set the 'exc' form field to 'n' when selected, 'y' otherwise //rules['.navSrch input#geo'] = function(elmt) { // elmt.addEvent('change', function() {$('exc').value = elmt.checked ? 'n' : 'y';}); //} // collapsible content - hide initially var toggleMeRule = function(elmt) { if( false == elmt.hasClass('defaultOpen') ) { elmt.addClass('off'); } elmt.getElement('.show').addEvent('click', toggle.bindAsEventListener(elmt)); elmt.getElements('.hide').addEvent('click', toggle.bindAsEventListener(elmt)); } rules['#TOGGLEME'] = toggleMeRule; // to appease FF's XPath rules['#TOGGLEME2'] = toggleMeRule; // used by DHTML pop-ups // .js_swapBlocks is an open-only toggle ajaxRules['div.js_swapBlocks'] = function(elmt) { var swapOut = elmt.getElement('.js_swapOut'); var swapIn = elmt.getElement('.js_swapIn'); var swapTrigger = elmt.getElement('a.js_swapTrigger'); var swapBackTrigger = elmt.getElement('a.js_swapBackTrigger'); if(swapOut && swapIn && swapTrigger) { swapTrigger.addEvent('click', function(e) { new Event(e).preventDefault(); window.removedBit = swapOut.remove(); swapIn.show(); }); swapTrigger.removeClass('js_swapTrigger'); } if(swapOut && swapIn && swapBackTrigger) { swapBackTrigger.addEvent('click', function(e) { new Event(e).preventDefault(); swapIn.hide(); $('parentOfSwapOut').appendChild(window.removedBit); }); swapBackTrigger.removeClass('js_swapBackTrigger'); } } // MOVE - only used by nexus // Nexus - subscribe/unsubscribe var modifySub = function(e) { (new Event(e)).preventDefault(); var elmt = this; new Json.Remote(this.href, { onFailure: function(e) { alert(JS_Ajax_failed); }, onComplete: function(rslt) { if (rslt && rslt.debug && rslt.editErrorTag) alert("Error during ajax call, \nTag: " + rslt.editErrorTag + "\nMsg: " + rslt.editErrorMessage + "\nContent: " + data.editErrorContent); else if (rslt.readonly) document.location = rslt.maintenanceUrl; else if (rslt.loginUrl) document.location = rslt.loginUrl; else { var i = elmt.getParent().id; if (/^un/.test(i)) i = i.substring(2); else i = "un" + i; elmt.getParent().hide(); $(i).show(); } } }).send(); } // MOVE - only used by a couple servlets // type ahead rules['input.typeAhead[type=text]'] = function(elmt) { if (action = elmt.className.match(/\bact(\w+)\b/)) { var onSelectFunc = elmt.className.match(/\bonSel-([\w\.]+)\b/); if ((onSelectFunc != null) && (onSelectFunc.length > 0)) { onSelectFunc = eval(onSelectFunc[1]); } // check if we have a custom zIndex var zIndex = 42; // 42 is the default in Autocompleter.js var zIndexClass = elmt.className.match(/zIndex-(\d+)/); if (zIndexClass != null) { zIndex = zIndexClass[1]; } new Autocompleter.Ajax.Json2(elmt, "/TypeAheadJson?action="+action[1], { ajaxOptions: {method:'get'}, postVar: 'query', inheritWidth: false, 'zIndex':zIndex, onSelect: function(elemt, resObj) { if (onSelectFunc) { onSelectFunc(elemt, resObj, elmt); } } }); } } // used in DHTML pop-up ajaxRules['#PM_UNBLOCK'] = function(elmt) { elmt.addEvent('submit', function(e) { new Event(e).stop(); var form = $('PM_UNBLOCK'); if (form.unblock.value == '1') { new Ajax('/SendMessageRD', { data: form, onComplete: ta.overlays.showInLightbox, onFailure: function(e) { alert(JS_Ajax_failed); } }).request(); } else { $('unblock-intercept').hide(); $('blockedreply').show(); form.unblock.value = '1'; } }); } // Tank-Of-Gas city type ahead form //rules['#togGeoSearch'] = function(elmt) { // elmt.addEvent('click', function(e) { // var form = $("TOG_FORM"); // if (form.elements.q.value == "Enter City") // { // form.elements.q.value = ""; // } // }); //} // members only (member benefits) tank-of-gas form //rules['#TRAVEL_GUIDES_LANDER #TOG_FORM'] = function(elmt) { // elmt.addEvent('submit', function(e) { // processTOGForm(e); // }); //} // members only (member benefits) guide request form //rules['#MOG_FORM'] = function(elmt) { // elmt.addEvent('submit', function(e) { // processGuideRequest(e); // }); //} // members only (member benefits) subscription form // rules['#MEMONLY_SUBS_FORM'] = function(elmt) { // elmt.addEvent('submit', function(e) { // processMOSubscribeRequest(e); // }); // } function enableCommunity(callback, noHandle) { var res = callback; if (!noHandle) res = function(r) {enableCommunityResponse(r, callback);} new Ajax("/MemberSubscriptionsController?Action=ChangeM2M&set=on", { onComplete: res, onFailure: function(e) { alert(JS_Ajax_failed); } }).request(); } function enableCommunityResponse(res, callback) { if(txt.match(/^{/)) { var data = eval( '(' + txt + ')' ); if(data.ERROR) { document.getElementById('inviteStatusField').innerHTML = data.ERROR; document.getElementById('inviteStatusField').style.display='block'; } } else { document.getElementById('inviteStatusField').innerHTML = JS_community_on; document.getElementById('inviteStatusField').style.display='block'; //setTimeout(function() {hide('confirmBubble'); callback();}, 1000); } } var communityLightbox = function(uri, sCBFuncName) { if (!userLoggedIn || migrationMember) return; if (!communityEnabled) { window['callback_communityLightboxEnable'] = function() { communityEnabled = true; var lb = ta.retrieve('overlays.current'); if (lb) lb.hide(); communityLightbox(uri, sCBFuncName); }; var aSMC = new StringBuffer(); aSMC.append(''); aSMC.append('

'+JS_mem_travelnet_friends_disabledM2M+'.

'); // You have disabled member-to-member communications aSMC.append('

'+JS_common_Clickhere+' '+JS_mem_travelnet_friends_turnOn+'.

'); // Click here to turn the feature back on and send your message ta.overlays.showInLightbox(aSMC.toString()); return; } if (sCBFuncName) { var callback_communityLightbox = function(r) { ta.overlays.showInLightbox(r); var cbfn = window[sCBFuncName]; if ( $defined(cbfn) && $type(cbfn) == "function" ) { cbfn(); } }; } else { var callback_communityLightbox = function(r) { ta.overlays.showInLightbox(r); }; }; var ops = { onComplete: callback_communityLightbox, evalScripts: true, onFailure: function(e) { alert(JS_Ajax_failed); } }; new Ajax(uri, ops).request(); } function getRelativeURL() { return window.location.pathname + window.location.search + window.location.hash; } var ajaxMsg = function(e) { new Event(e).preventDefault(); var sCBFuncName; if (this.className.match(/\b(callback_ShowRecaptcha)\b/)) { sCBFuncName = RegExp["$1"]; } if (!userLoggedIn || migrationMember) { if (!(ops = this.className.match(/\bpm(\d)([A-F0-9]+)\b/))) return; var msgType = parseInt(ops[1]) == 1 ? "cc" : "cm"; var greeting = msgType == "cm" ? "m2m_singin_greeting_d99" : ""; var uid = ops[2]; var cookieVal = "communityLightbox|" + this.href; if (sCBFuncName) { cookieVal += "|" + sCBFuncName; } Cookie.set('ajaxAction', cookieVal, {domain:cookieDomain, path:"/"}); var func = !userLoggedIn ? login : migrate; func(['tt',msgType,'greeting',greeting,'returnTo', getRelativeURL()]); return; } communityLightbox(this.href, sCBFuncName); } var ajaxReport = function(e) { new Event(e).preventDefault(); ta.overlays.loadInLightbox(this.href); } var follow = function(id) {return function(){window.location = $(id).href;}} //rules['#REVIEWS span.fkLnk,#TOURISMREVIEWS span.fkLnk'] = function(elmt) { // if (/\bt([\w\d]+)\b/.test(elmt.className)) elmt.addEvent('click', follow(RegExp.$1)); //} rules['#dhtmlPopupClose']=function(elmt) { elmt.addEvent('click', function() { $('DHTMLPOPUP').remove(); $('dhtmlPopupIframe').remove(); }); } var reviewRating = new Class( { initialize: function(userId, rating) { this.userId = userId; this.rating = rating; } }); var dualSliderRule = function(elmt) { var sDivOne = elmt.getElement('.sOne'); var sDivTwo = elmt.getElement('.sTwo'); var minMax = elmt.getElement('.minMax'); var sMin = elmt.getElement('.rangeMin'); var sMax = elmt.getElement('.rangeMax'); var sLeft = elmt.getElement('.sLeft'); var sRight = elmt.getElement('.sRight'); var sldrSteps = parseInt(elmt.className.match(/s(\d+)/)[1]); var sldrMin = parseFloat(elmt.className.match(/mn([\d.]+)/)[1]); var sldrMax = parseFloat(elmt.className.match(/mx([\d.]+)/)[1]); var selMin = parseFloat(elmt.className.match(/smin([\d.]+)/)[1]); var selMax = parseFloat(elmt.className.match(/smax([\d.]+)/)[1]); var sName = elmt.className.match(/name(\w+)/)[1]; var maxAllInclusive = elmt.hasClass('maxAllInclusive'); var sOffset = /off(\d+)/.test(elmt.className) ? parseInt(RegExp.$1) : 1; if(elmt.className.match(/cur(\d+)/)) { crncy = currencyCodes[ elmt.className.match(/cur(\d+)/)[1] ]; } var doRounding = elmt.className.match(/(round)/); if(doRounding) doRounding = true; if (sDivOne && sDivTwo) { var slider = new DualSlider(elmt, sDivOne, sDivTwo, { "maxAllInclusive":maxAllInclusive, onChange: function() { var valOne = sliderStepToValue(sldrMin, sldrMax, sldrSteps, slider.knobOne.step, slider.options.round); var valTwo = sliderStepToValue(sldrMin, sldrMax, sldrSteps, slider.knobTwo.step, slider.options.round); // if the values have not changed then mark the hidden field value accordingly (since we use 0 & 999999 to // indicate no selection as opposed to the boundary values which imply an explicit selection). var minMaxLow = valOne < valTwo ? valOne : valTwo; var minMaxHigh = valTwo < valOne ? valOne : valTwo; if (minMaxLow == sldrMin) minMaxLow = 0; if (minMaxHigh == sldrMax) minMaxHigh = 999999; minMax.value = minMaxLow + "," + minMaxHigh; var sLower = (valOne <= valTwo) ? valOne : valTwo; var sUpper = (valOne <= valTwo) ? valTwo : valOne; // currency slider if(slider.options.name == 'pslider') { sMin.innerHTML = ta.util.currency.formatCurrency(sLower, crncy); var sMaxText = ta.util.currency.formatCurrency(sUpper, crncy); if(slider.options.maxAllInclusive && (sUpper == sldrMax)) { sMaxText += "+"; } sMax.innerHTML = sMaxText; } // rating slider else if(slider.options.name == 'rslider') { if( (sLower+"").length == 1) sLower = sLower + ".0"; sMin.innerHTML = sLower; if( (sUpper+"").length == 1) sUpper = sUpper + ".0"; sMax.innerHTML = sUpper; var pattern = /(^.*)(\d\.\d)(.gif)/; updateRatingSrc(sLeft, pattern, true, sLower); updateRatingSrc(sRight, pattern, true, sUpper); } // star slider else if(slider.options.name == 'sslider') { sMin.innerHTML = sLower; sMax.innerHTML = sUpper; var pattern = /(^.*)(\d)(.gif)/; if(sLower >= 1 && sLower <= 5) //bug 27384 { updateRatingSrc(sLeft, pattern, false, sLower); } if(sUpper >= 1 && sUpper <= 5) //bug 27384 { updateRatingSrc(sRight, pattern, false, sUpper); } } // integer slider else if (slider.options.name == 'islider') { sMin.innerHTML = sLower; sMax.innerHTML = slider.options.maxAllInclusive && (sUpper == sldrMax) ? sUpper + "+" : sUpper; } }, onComplete: ($defined(window['sliderMoved']) ? sliderMoved : Class.empty), steps: sldrSteps-1, offset: sOffset, round: doRounding, name: sName }); slider.setKnobFromValue(sDivOne, selMin, sldrMin, sldrMax, sldrSteps); slider.setKnobFromValue(sDivTwo, selMax, sldrMin, sldrMax, sldrSteps); slider.sliderOptions = { knobs: [sDivOne, sDivTwo], min: sldrMin, max: sldrMax, step: sldrSteps }; elmt.slider = slider; } } rules['#LEFTNAV .fltr div.dualSliderTest'] = dualSliderRule; // rules['#HAC_FORM div.dualSliderTest'] = dualSliderRule; function updateRatingSrc(elem, pattern, isDec, newVal) { var elemMatch = elem.getElement('.lmtImg').src.match(pattern); if(elemMatch) { if( (newVal+"").length == 1 && isDec) newVal = newVal + ".0"; elem.getElement('.lmtImg').src = elemMatch[1] + newVal + elemMatch[3]; } } function sliderStepToValue(min, max, totalSteps, currentStep, round) { if(round) return Math.round( currentStep * ( (max - min) / (totalSteps - 1) ) ) + min; return currentStep * ( (max - min) / (totalSteps - 1) ) + min; } var currencyHash = new Object(); function buildCurrencyHash(ind, min, max, selMin, selMax, steps, maxAllInclusive) { currencyHash[ind] = { 'min' : min, 'max' : max,'selMin' : selMin,'selMax' : selMax, 'steps' : steps, 'maxAllInclusive' : maxAllInclusive}; } var weeklyCurrencyHash = new Object(); function buildWeeklyCurrencyHash(ind, min, max, selMin, selMax, steps) { weeklyCurrencyHash[ind] = { 'min' : min, 'max' : max,'selMin' : selMin,'selMax' : selMax, 'steps' : steps}; } // used to submit the page when the user toggles "Original in " or "Automatic Translation" radio buttons // have to use the onclick event because IE6 doesn't handle onChange properly with radio buttons //rules['input.submitOnClick[type=radio]'] = function(elmt) //{ // elmt.addEvent('click', function() // { // elmt.form.submit(); // }); //} // Name of the cookie used to track the distance units for "Nearby Locations" var distanceUnitsCookieName = "TAdistanceCookie"; // rules['#NEARBY .js_miOp']=function(elmt) // { // elmt.addEvent('click', showMi.bindAsEventListener(elmt)); // } // rules['#NEARBY .js_kmOp']=function(elmt) // { // elmt.addEvent('click', showKm.bindAsEventListener(elmt)); // } rules['#NEARBY #nearbyDistanceUnits']=function(elmt) { Cookie.set(distanceUnitsCookieName, $(elmt).value); } // Attempt a popUp on window load, on failure show link in lightbox -dkw rules['#js_tryPop'] = function(tryPop) { var lnk = tryPop.getElement('a.js_popLink'); var ops = "toolbar=0,resizable=1,menubar=0,location=0,status=0,scrollbars=1"; //link to try if(lnk) { if(!window.open( lnk.href, lnk.name, ops)) { // lightbox message if(tryPop.getElement('div.js_lightBoxMsg')) window.addEvent( 'load', function() { ta.overlays.showInLightbox(tryPop.getElement('div.js_lightBoxMsg').innerHTML) } ); } } } // Log a Review Link click for reporting rules['#REVIEW_LINK']=function(elmt) { elmt.addEvent('click', reviewLinkClick.bindAsEventListener(elmt));}; function reviewLinkClick(pid) { var pid = $('REVIEW_LINK').className.match(/pid(\d+)/) ; if (pid && pid[1]) { Cookie.set('NPID', pid[1], {domain: cookieDomain, time:5}); new Ajax('/ActionRecord?action=review_click').request(); } } rules['#memberFlyout .memberBenefitsPopup'] =function(elmt) { elmt.addEvent('click', function(e) { new Event(e).preventDefault(); showDHTMLPopup('MemberBenefitsPopup', '/MemberBenefitsPopup', true) } ); } // this must be declared here var popupConfig = {}; function addNonMember(e, elmt) { if (!elmt) elmt = this; popupConfig.clickedElement = elmt; new Event(e).preventDefault(); // if the clicked element has a pid then add that to the url var url = '/MemberBenefitsPopup'; if (/pid(\d+)/.test(elmt.className)) { url += '?pid='+RegExp.$1; } showDHTMLPopup('MemberBenefitsPopup', url, true); } function clickNonMember() { setTimeout("window.lightbox.deactivate()", 500); if (popupConfig.clickedElement == null) return; var c = popupConfig.clickedElement; popupConfig.clickedElement = null; $$('#TRAVEL_GUIDES_LANDER .clickNonMember').each( function(el, i) { el.removeEvent('click', addNonMember); }); $$('#TRAVEL_GUIDES_FLY .clickNonMember').each( function(el, i) { el.removeEvent('click', addNonMember); }); if (c.className.match(/\bclickPopDestMOGLink\b/)) processPopDestMOGLink(null, c); else if (c.getTag() == "a") window.location= c.getProperty('href'); else c.click(); } function processPopDestMOGLink(e, elmt) { if (!elmt) elmt = this; if (!loggedIn) addNonMember(e,elmt); else processGuideLink(e,elmt); } // members only non-member click rules['#TRAVEL_GUIDES_LANDER .clickNonMember'] = rules['#TRAVEL_GUIDES_FLY .clickNonMember'] = function(elmt) { if (elmt.getTag() != 'a' || ! elmt.className.match(/\bskipATag\b/)) { elmt.addEvent('click', addNonMember); }; } // members only popular destination guide link click rules['#TRAVEL_GUIDES_LANDER .clickPopDestMOGLink'] = function(elmt) { elmt.addEvent('click', processPopDestMOGLink); } // redesign popup logic // new popup function DHTMLOverlayRequest(action, responseFunction, errorFunction, formElement) { var sep = "?"; if (popupConfig.servletUrl.contains(sep)) sep = "&"; var url = popupConfig.servletUrl+sep+"Action="+action+"&fromServlet=" + pageServlet; new Ajax(url, { onComplete: responseFunction, onFailure: errorFunction, data: formElement, evalScripts: true }).request(); } // Call any automatic popups on page load. This should be called explicitly. // To call manually use showDHTMLPopup(popupServlet, popupServletUrl, popupLightbox) // The variable $popupServlet is decided by the PopupManager.java and shouldn't be set explicitly function showDHTMLOverlayOnLoad() { if(!doCookieCheck()) { return; } if (isOverlayServlet) { if( overlayOptions == "false" ) { var opts = {}; } else { var opts = overlayOptions; } opts.permanent = true; showDHTMLPopup(isOverlayServlet, overlayServletUrl, overlayLightbox, opts); } if (IS_OVERLAY_DEBUG && typeof makePopupSummary != 'undefined') { makePopupSummary(); } } // This is the preferred way of showing a Dhtml Popup // PopupManager.java uses these functions along with some velocty stuff in header_popup.vm // Note that this means on load popups setup by the servlet! These functions should not be called // in response to a user action! Use ta/overlays/Factory.js for that! function showDHTMLPopup(popupServlet, popupServletUrl, popupLightbox, ops, e) { popupConfig.servlet = popupServlet; popupConfig.servletUrl = popupServletUrl; popupConfig.lightbox = popupLightbox; popupConfig.options = ops; if (ops && ops.loading && e) { e = new Event(e); $(ops.loading).show().injectInside(document.body).setStyles({ left: e.page.x + 20, // to the right of the mouse top: e.page.y }); } DHTMLOverlayRequest("Open", DHTMLOverlayResponse, null, ops ? ops.data : null); } var DHTMLOverlayResponse = function(content) { if (popupConfig.options && popupConfig.options.forward && window.popupForward) { window.location = window.popupForward; } // make sure we're not showing any overlays $$('#FLYOUT').each(function(item, index) {item.remove();}); window.lightbox.deactivate(); $$('.overlay').each(function(item, index){ if(item.overlay && item.overlay.hide) { item.overlay.hide(); } else if(item.hide) { item.hide(); } else { item.remove(); } }); // hide the loading layer if (popupConfig.options && popupConfig.options.loading) $(popupConfig.options.loading).hide(); var overlay; var overlayOps = {}; if (popupConfig.lightbox) { overlayOps.backdrop = ta.overlays.BACKDROP_ALWAYS; } if (popupConfig.options && popupConfig.options.delayedPosition) overlayOps.delayedPosition = popupConfig.options.delayedPosition; if (popupConfig.options && popupConfig.options.permanent) { overlayOps.xOffset = $('PAGE').getLeft() + 100; overlayOps.yOffset = $('PAGE').getTop() + 220; overlay = new ta.overlays.PermanentOverlay(overlayOps); } else if (popupConfig.options && popupConfig.options.center) { overlay = new ta.overlays.CenteredOverlay(overlayOps); } else { overlayOps.xOffset = $('PAGE').getLeft() + 100; overlayOps.yOffset = $('PAGE').getTop() + 220; overlay = new ta.overlays.AbsoluteOverlay(overlayOps); } overlay.loadRemoteSuccess(content); var popupOnHideHandler = function() { if (typeof dhtmlRedirectLink != 'undefined') { window.location = dhtmlRedirectLink; return; } if (this.inner.getElement('.refreshOnClose')) { document.location.reload(); return; } if (typeof addPopupCloseClass != 'undefined' && !this.inner.getElement('.silentClose')) DHTMLOverlayRequest('Close'); if (this.inner.getElement('.popupBackOnClose')) { if (/(?:^|;)\\s*TAReturnTo=\%1\%([^;]*)/.test(document.cookie)) window.location = decodeURIComponent(RegExp.$1); else window.location = "/"; } } overlay.addEvent('onHide', popupOnHideHandler); if (popupConfig.servlet) { // give us control to apply styles depending on which popup we're showing overlay.container.addClass(popupConfig.servlet); // a subset of the actions defined in DhtmlPopup.java // only actions that are triggered by a mouse click overlay.inner.getElements('.popupConvert').each(function(item, index){ item.addEvent('click', function(){ DHTMLOverlayRequest('Convert'); } ); }); overlay.inner.getElements('.popupDecline').each(function(item, index){ item.addEvent('click', function(){ DHTMLOverlayRequest('Decline'); } ); }); overlay.inner.getElements('.dhtmlclose').each(function(item, index){ item.addEvent('click', function(){ var o = overlay; o.hide(); } ); }); var popupSubmitOnEnterFunction = function(e) { var charCode = (e.charCode) ? e.charCode : ((e.which) ? e.which : e.keyCode) if(charCode == 13 || charCode == 10) { popupSubmitFunction(e); } } var popupSuppressEnterFunction = function(e) { var charCode = (e.charCode) ? e.charCode : ((e.which) ? e.which : e.keyCode) if(charCode == 13 || charCode == 10) { new Event(e).preventDefault(); } } overlay.inner.getElements('.popupSubmitOnEnter').each(function(item, index){ item.addEvent('keydown', popupSubmitOnEnterFunction ); }); var popupSendForgotEmailFunction = function(e) { $$('#POPUP_FORM #sendpass').setProperty("value", "true"); popupSubmitFunction(e); } overlay.inner.getElements('.popupSendForgotEmail').each(function(item, index){ item.addEvent('click', popupSendForgotEmailFunction ); }); // avoid duplicate submits var popupSubmitFunction = function(e) { new Event(e).preventDefault(); overlay.inner.getElements('.popupSubmit').each(function(item, index){ item.removeEvent('click', popupSubmitFunction); }); overlay.inner.getElements('.popupSendForgotEmail').each(function(item, index){ item.removeEvent('click', popupSendForgotEmailFunction); }); overlay.inner.getElements('.popupSubmitOnEnter').each(function(item, index){ item.removeEvent('keydown', popupSubmitOnEnterFunction); item.addEvent('keydown', popupSuppressEnterFunction); }); overlay.inner.getElements('.focusClear').each(function(item, index){ item.setProperty('value', ''); }); if (popupConfig.options && popupConfig.options.disableOnHideForSubmit) { overlay.removeEvent('onHide', popupOnHideHandler); } DHTMLOverlayRequest('Submit', DHTMLOverlayResponse, null, overlay.inner.getElementById('POPUP_FORM')); } overlay.inner.getElements('.popupSubmit').each(function(item, index){ item.addEvent('click', popupSubmitFunction ); }); } if (popupConfig.options && popupConfig.options.callback) { popupConfig.options.callback(overlay.container); } } function moveDHTMLPopupToScrollPosition(event) { var popupHeight = this.getSize().size.y; // popup is bigger than whole window so just don't move it if (popupHeight > window.getHeight()) { return; } // keep it centered else if (popupConfig.options && popupConfig.options.center) { var cs = this.getSize(); this.setStyles({ left: (window.getWidth() - cs.size.x) / 2 + window.getScrollLeft(), top: (window.getHeight() - cs.size.y) / 2 + window.getScrollTop() }); } // top offset pushes it out of window else if (popupConfig.coords.top + popupHeight > window.getHeight()) { var freeSpace = window.getHeight() - popupHeight; this.setStyle('top', window.getScrollTop() + (freeSpace/2) + 'px'); } // keep the offset to top defined in base.css else { this.setStyle('top', window.getScrollTop() + popupConfig.coords.top + 'px'); } } // Bug 27446 if (window.opener == null && typeof(cpu_enabled) != "undefined") { var cpu_paused = false; var cpu_go = function(e, triggerType) { if (cpu_paused && triggerType == 'unload') return; if (! doCookieCheck()) { return; } // Check to make sure it's a link that was clicked, // only check if this is the onclick version if (triggerType == 'click') { e = new Event(e || window.event); var eTarget = $(e.target); var eTargParentA = eTarget.getParent('a'); if( eTarget.getTag() != 'a' && eTargParentA == null ) { return; } if( ( eTarget.className && eTarget.className.match(/\b(js_pop)/) ) || ( eTargParentA != null && eTargParentA.className && eTargParentA.className.match(/\b(js_pop)/) ) || eTarget.getParent('#CRUISE_CRITIC_REVIEWS_FORM') ) { return; } if( eTarget.onclick && eTarget.onclick != null && eTarget.onclick.toString().match(/window\.open/g)) { return; } } var alreadyPopped = Cookie.getRaw('CommercePopunder'); if (alreadyPopped) { // We want to suppress unless the trigger type is a click and we have a SuppressUnload cookie if (alreadyPopped != 'SuppressUnload' || triggerType != 'click') { return; } } if(blockPU) { return; } //don't spawn MPU if coming from Flights servlet if(IS_MPU_ENABLED && "Flights" == pageServlet) { return; } // check if commerce click happened var csv = Cookie.getRaw('TASession'); if (csv && /\*PD(\d+)\.(\d+)/.test(csv) && /\*CC\.(\d+)/.test(csv) && !IS_MPU_ENABLED) { new Ajax("/ActionRecord?action=AbortedOpen_expediaOrbitzCommerceClick").request(); return; } //Double check for data in cookie (user may have deleted cookies mid-session) // bug 28579 if((!csv || !(/\*LL\.(\d+)/.test(csv))) && !IS_MPU_ENABLED) // if no cookie or no commerce-valid loc data in cookie { new Ajax("/ActionRecord?action=AbortedOpen_noCookieData").request(); return; } var w = 1; var h = 1; if (window.ie7) {w = 250; h = 100;} if (window.webkit) { w = 85; h = 100;} var nX = (window.screenLeft || window.screenX); var nY = (window.screenTop || window.screenY); if (typeof(nX) == "undefined") { // full screen nX = 0; nY = 0; var x = nX + window.screen.availWidth - w; // bottom-right corner of window var y = nY + window.screen.availHeight - h; } else{ var x = nX + window.getWidth() - w; // bottom-right corner of window var y = nY + window.getHeight() - h; } if(window.ie7) { y = y - 95; x = x + 13; } else if(window.webkit) { y = y - 16; } else { x = 5000; y = 5000; } var cookieDuration = 5; if(IS_MPU_ENABLED) { cookieDuration = 3; } Cookie.set("CommercePopunder", "SuppressAll", {domain: cookieDomain, duration: cookieDuration}); var backupParam = ""; if(overlayBackupLoc) { backupParam = "&backupLoc=" + overlayBackupLoc; } var encoded_cpu_url = escape(window.location.href); var cpu_uri = 'http://'+window.location.hostname+'/CommercePopunderEnhanced?mainWindowReturnTo=' + encoded_cpu_url + '&fromServlet=' + pageServlet + backupParam; var width=790; var height=660; if(IS_MPU_ENABLED && "CheapFlights" != pageServlet) { var rand = $random(1,2); cpu_uri = cpu_uri + "&flights="+rand; width = 715; if (rand=="1") { height = 450; } else { height = 500; } } if (triggerType == 'click') { cpu_uri = cpu_uri + "&onclick=1" } // record attempt to launch popunder cpu_win = window.open(cpu_uri, "CommercePopunder", "toolbar=1,location=1,directories=1,status=1,menubar=1,resizable=1,copyhistory=1,scrollbars=1,width="+w+",height="+h+",left="+x+",top="+y); if (cpu_win != null) { cpu_win.opener = self; cpu_win.blur(); window.focus(); cpu_win.moveTo(nX + (window.getWidth() - width) / 2, nY + (window.getHeight() - height) / 2); cpu_win.resizeTo(width,height); } else { // Can't rely on an Ajax during an unload new Asset.image('/ActionRecord?action=fail_commercePopunderBlocked_' + triggerType); // If this was an unload CPU... if (triggerType == 'unload') { // Then suppress future unloads (allowing clicks) Cookie.set("CommercePopunder", "SuppressUnload", {domain: cookieDomain, duration: cookieDuration}); } } } // end of cpu_go function var cpu_resume = function() {window.cpu_paused = false;} // runs when user clicks in page var cpu_pause = function(e) { e = new Event(e || window.event); var eTarget = $(e.target); // if the user clicks within the site, prevent popunder from showing before next page to load // logic is user is navigating within the site or clicking commerce to leave the site if (!cpu_paused && (eTarget.getTag() == 'a' || eTarget.getParent('a') != null)) { window.cpu_paused = true; cpu_resume.delay(1000); } } // runs on load var cpu_run = function() { // Add handlers for unload window.addEvent('beforeunload', function (e) { cpu_go (e, 'unload'); } ); document.addEvent('click', cpu_pause); // Add handler for onclick document.addEvent('click', function (e) { cpu_go (e, 'click'); } ); } } function resizeToWindow(event) { var pageSize = Math.max( window.getHeight(), window.getScrollHeight() ); this.setStyle('height', pageSize + 'px'); } function showToggleBlock(toggleClass) { // only hide all the toggle blocks besides toggleClass $$('.js_toggleBlocks .js_toggleBlock').each( function(el, i) { el.hide(); if (el.hasClass(toggleClass)) { el.show(); } }); } linkMap['js_reopenDhtmlPopup']=function(elmt, e) { new Event(e).preventDefault(); showDHTMLPopup(popupConfig.servlet, popupConfig.servletUrl, popupConfig.lightbox); } linkMap['js_toggleBlockTrigger'] =function (elmt, e) { var toggleClass = elmt.getProperty("class"); toggleClass.replace(/toggleBlock[0-9]+/g, function(match){ toggleClass = match; }); showToggleBlock(toggleClass); }; rules['#SWAPBLOCK .js_toggleBlock']=function(elmt) { // the default toggleBlock if (elmt.hasClass("js_toggleBlockSelected")) { var toggleClass = elmt.getProperty("class"); toggleClass.replace(/toggleBlock[0-9]+/g, function(match){ toggleClass = match; }); showToggleBlock(toggleClass); } } function clearPopupForm() { // clear password $$('#POPUP_FORM #pass').setProperty("value", ""); // clear errors $$('#DHTMLPOPUP .error-message').empty(); } // only used in DHTML pop-ups linkMap['js_clearPopupForm'] =function (elmt, e) { clearPopupForm(); }; function setPopupFormAction(val) { $$('#POPUP_FORM #action').setProperty("value", val); } // only used in DHTML pop-ups linkMap['js_popupFormAction1'] =function (elmt, e) { setPopupFormAction("1"); }; linkMap['js_popupFormAction2'] =function (elmt, e) { setPopupFormAction("2"); }; function callPopupSendPasswordEmail(elmt) { alert("callPopupSendPasswordEmail"); $$('#POPUP_FORM #sendpass').setProperty("value", "true"); popupSubmitFunction(elmt); } // only used in DHTML pop-ups ajaxRules['.js_popupForgot']=function(elmt) { elmt.addEvent("click", callPopupSendPasswordEmail); } // MOVE - only used in Help Center rules['#HELP_CENTER dl.js_toggleset'] = function(elmt) { var initTab = 0; var ts = new ToggleSet(elmt.getElements('dt'), elmt.getElements('dd')); if (window.location.hash && (hash = window.location.hash.match(/c(\d+)/))) ts.display(parseInt(hash[1])); } // XXX - used for pool testing of amenity icons // This should be removed in the not so distant future // Greg Belote 19aug08 var amenityFiredHoverEvent = false; function recordAmenityIconHover () { // send an AJAX request that will log the hover new Ajax("/ActionRecord?action=amenityIconHover").request(); // note that we've recorded one hover for this page view amenityFiredHoverEvent = true; } //rules['#redesignAmenity .amenities img'] = function(icon) { // icon.addEvent('mouseenter', function(e) { // // if we haven't already recorded a hover for this page view... // if (amenityFiredHoverEvent == false) // { // // set a timer at 750ms to record the user hovering // icon.hoverTimeout = setTimeout ("recordAmenityIconHover()", 750); // } // }); // // icon.addEvent('mouseleave', function(e) { // // stop the timeout // clearTimeout (icon.hoverTimeout); // // // remove any references to it // icon.hoverTimeout = null; // }); //} var propertyTypeClicked = false; //rules['#PROPERTY_TYPE .flyout a'] = function(questionMark) //{ // questionMark.addEvent('click', function(e) { // if (propertyTypeClicked == false) // { // new Ajax("/ActionRecord?action=propertyTypeClicked").request(); // propertyTypeClicked = true; // } // }); //} var mtHelpHovered = false; rules['#ACCOM_DETAIL div.translation li.flyoutB a, #SHOW_USER_REVIEW div.translation li.flyoutB a'] = function(questionMark) { questionMark.addEvent('mouseenter', function(e) { if (mtHelpHovered == false) { new Ajax("/ActionRecord?action=MTQuestionmarkHover").request(); mtHelpHovered = true; } }); } rules['#HAC_FORM .js_hacRadio'] = function(elmt) { if(!elmt.checked) { elmt.addEvent("click", function(e) { var aelmt = $('cat' + elmt.value + 'url'); if(aelmt) { window.location = aelmt.href; } }); } } ajaxRules['#HotelDateSearch_CR ul.siteLst'] = function(elmt) { if(typeof vendors != "undefined") { vendors.each(function(vendor) { var bn_checked = ""; if($(vendor.name).checked) { bn_checked = ' checked="checked"'; } new Element('li').setHTML( '' + '' ).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 0) { selectedTabIndex = parseInt(hash[1]); selectedTab.removeClass('current'); elmt.getElements('.tab')[selectedTabIndex].addClass('current'); } } // grab references to all tabs and tab content nodes var tabs = elmt.getElements('.tabContainerHead .tab'); if (tabs.length == 0) return; var elmts = elmt.getElements('.tabContainerBody .tabContent') // create a new tab set new TabSet(tabs, elmts, { show:selectedTabIndex, onActive:function(event, toggler, element) { var xhr = toggler.hasClass('xhr'); // if we are using XHR to populate the tab content and we have not already done so if (xhr && !toggler.hasClass('xhrComplete')) { // grab the href var href = toggler.getElement('a').href; if (href) { // replace the contents of the tab element via XHR var myToggler = toggler; element.addEvent('onContentReplaced', function(){myToggler.addClass('xhrComplete');}); replaceContent(event, element, href); } } // if we should record the user action if (toggler.hasClass('recordAction')) { new Ajax('/ActionRecord?action=' + toggler.id).request(); } // add a class to mark this as the current tab toggler.addClass('current'); // if we are using XHR then stop the event so we do not navigate the entire page if (xhr && event) event.stop(); }, onBackground:function(event, toggler, element) { toggler.removeClass('current'); } }); }; //close the lightbox (for 'account has been deleted' lightbox) rules['#LIGHTBOX_CLOSE'] = function(elmt) { elmt.addEvent("click", function(e) { var lb = ta.retrieve('overlays.current'); if (lb) lb.hide(); }); }; //Expand click area for offers in BBDN box on Tourism Servlet //rules['#BBD tr.offerRow'] = function(elmt) { // elmt.addEvent('click', function(e) { // var event = new Event(e); // event.preventDefault(); // offerLink = elmt.getElement('a'); // popCommFN = linkMap['js_popComm']; // popCommFN(offerLink,e); // }); //} // open the vacation rentals calculator in a lightbox function vrCalculator (elmt, e) { var event = e != null ? new Event(e) : null; if (elmt && /pid(\d+)/.test(elmt.className)) Cookie.set('NPID', RegExp.$1, {domain: cookieDomain, time:5}); // if this is the home page promo var args = ''; var vrCalcForm = $('homePageVRCalcPromo'); if (!vrCalcForm) { // try looking for the div from the VR overview page vrCalcForm = $('calculatorOverviewPromo'); } if (!vrCalcForm) { // try looking for the div from the VR overview page vrCalcForm = $('calculatorLanderPromo'); } if (vrCalcForm && $('vrCalculatorForm')) { // if the user did not provide a geo name, clear the tip text var geoNameField = vrCalcForm.getElement('#geoName'); if (geoNameField.hasClass('focusClear')) { geoNameField.value = ''; geoNameField.removeClass('focusClear'); } // grab our args args = '&geoName=' + vrCalcForm.getElement('#geoName').value; } // open an ajax lightbox and when active focus on the first input element new ta.overlays.CenteredOverlay({ backdrop: ta.overlays.BACKDROP_ALWAYS, onReady: function() { this.inner.getElement('input').focus();} }).loadRemoteContent('/VacationRentalCalculator?restart=true' + args); if (event) event.stop(); return false; }; // update the vacation rentals calculator with results linkMap['js_submitVRCalculator'] = function(elmt, e) { var event = e != null ? new Event(e) : null; // get the calculator div and form var div = $('vrCalculator'); var form = div.getElement('form'); // hide any server error messages since they are trying again var serverError = div.getElement('.serverErrorMsg'); if (serverError) { serverError.style.display = 'none'; } // make sure they selected a valid destination if (form.geoName.value == '') { div.getElement('.errorMsg').style.display = ''; } // else assemble a url from the form and replace the div's contents with the resulting // content else { var url = form.action + '?' + form.toQueryString(); replaceContent(e, div.getParent(), url); } if (event) event.stop(); return false; }; //pulled from lib/fbconnect.js for inclusion in tripadvisor.js /* * Copyright (c) 2008 Aza Raskin (http://azarask.in/blog) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ var SocialHistory = function() { // associative array of sites we're interested in // { site: [variations1, variation2...], site: [ ... } var sites = { "f": ["http://www.facebook.com/", "https://login.facebook.com/login.php", "http://www.facebook.com/home.php"] //"f": ["http://www.facebook.com/"] }; var visited = {}; function getStyle(el, scopeDoc,styleProp) { if (el.currentStyle) var y = el.currentStyle[styleProp]; else if (window.getComputedStyle) var y = scopeDoc.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp); return y; } function remove( el ) { el.parentNode.removeChild( el ); } // Code inspired by: // bindzus.wordpress.com/2007/12/24/adding-dynamic-contents-to-iframes function createIframe() { var iframe = document.createElement("iframe"); iframe.style.position = "absolute"; iframe.style.visibility = "hidden"; document.body.appendChild(iframe); // Firefox, Opera if (iframe.contentDocument) iframe.doc = iframe.contentDocument; // Internet Explorer else if(iframe.contentWindow) iframe.doc = iframe.contentWindow.document; // Magic: Force creation of the body (which is null by default in IE). // Also force the styles of visited/not-visted links. iframe.doc.open(); iframe.doc.write(''); 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