1/* @license GPL-2.0-or-later https://www.drupal.org/licensing/faq */ 2(function(Drupal,once,drupalSettings){'use strict';const visibleBodyClass='conversion-banner-visible';const revealThreshold=75;function getPreferredBanner(wrapper){return wrapper.querySelector('.js-conversion-banner[data-property-specific="true"]')||wrapper.querySelector('.js-conversion-banner');}function prepareMobileMessages(banner){banner.querySelectorAll('.js-conversion-banner-message').forEach((message)=>{const links=message.querySelectorAll('a[href]');const link=links.length===1?links[0]:null;const hasMobileMessage=link&&link.getAttribute('href').trim()!==''&&link.textContent.trim()!=='';message.classList.toggle('has-mobile-message',hasMobileMessage);if(!hasMobileMessage)return;const fullMessage=document.createElement('div');fullMessage.classList.add('conversion-banner__full-message');while(message.firstChild)fullMessage.appendChild(message.firstChild);const mobileMessage=document.createElement('a');mobileMessage.classList.add('conversion-banner__mobile-message');mobileMessage.setAttribute('href',link.getAttribute('href'));mobileMessage.textContent=link.textContent.trim();['hreflang','target','rel'].forEach((attribute)=>{const value=link.getAttribute(attribute);if(value)mobileMessage.setAttribute(attribute,value);});message.append(fullMessage,mobileMessage);});}function mountCarousel(banner){const carousel=banner.querySelector('.js-conversion-banner-carousel');if(!carousel||typeof Splide==='undefined')return;const slider=new Splide(carousel,{type:'loop',pagination:false,keyboard:'focused',speed:850,i18n:{prev:Drupal.t('Previous promotion'),next:Drupal.t('Next promotion')}});slider.mount();}function showPreview(banner){const wrapper=banner.closest('.c-site-alerts');if(wrapper)wrapper.style.display='block';banner.style.display='block';banner.classList.add('conversion-banner--preview','is-visible');banner.setAttribute('aria-hidden','false');}function initializeReveal(banner){let frameRequested=false;const update=()=>{frameRequested=false;const isRendered=banner.getClientRects().length>0;const scrollPosition=window.scrollY||window.pageYOffset;const hasFocus=banner.contains(document.activeElement);const isVisible=isRendered&&(scrollPosition>=revealThreshold||hasFocus);banner.classList.toggle('is-visible',isVisible);banner.setAttribute('aria-hidden',isVisible?'false':'true');document.body.classList.toggle(visibleBodyClass,isVisible);};const requestUpdate=()=>{if(frameRequested)return;frameRequested=true;window.requestAnimationFrame(update);};window.addEventListener('scroll',requestUpdate,{passive:true});window.addEventListener('resize',requestUpdate);window.addEventListener('pageshow',requestUpdate);banner.addEventListener('focusout',requestUpdate);requestUpdate();}Drupal.behaviors.conversionBanner={attach(context){once('conversionBanner','.js-conversion-banner',context).forEach((banner)=>{const wrapper=banner.closest('.c-site-alerts')||document;if(getPreferredBanner(wrapper)!==banner){once.remove('conversionBanner',banner);return;}prepareMobileMessages(banner);const previewMode=Boolean(drupalSettings.verb_site_alert&&drupalSettings.verb_site_alert.previewMode);if(previewMode){showPreview(banner);mountCarousel(banner);return;}mountCarousel(banner);initializeReveal(banner);});}};})(Drupal,once,drupalSettings);; 3(function (Drupal, once) { 4 'use strict'; 5 6 // Booking links Drupal does not build: anything an editor pasted into a 7 // WYSIWYG or link field still points at SynXis or AZDS. When that link is 8 // for a Dolli property, its href is rewritten to /book/{hotelCode} so a 9 // right-click or "open in new tab" reaches Dolli too, and the dolliBookingLinks 10 // behavior in dollieBooking.js then opens the widget on a left-click. 11 // Links for non-Dolli properties are left alone. 12 // 13 // Runs on every page (attached with drupalSettings.dolli_properties in 14 // viceroy_preprocess_region__header()), and must attach before 15 // dolliBookingLinks, which is why dollieBookingWidget depends on it. 16 Drupal.behaviors.dolliLinkRewrite = { 17 attach: function (context, settings) { 18 const dolliProperties = settings?.dolli_properties; 19 if (!dolliProperties) return; 20 21 once( 22 'dolli-link-rewrite', 23 'a[href*="reservations.viceroyhotelsandresorts.com"], a[href*="be.synxis.com"], a[href*="#/booking/step-1"]', 24 context 25 ).forEach((link) => { 26 const url = new URL(link.href, window.location.origin); 27 const isAzds = url.hash.indexOf('#/booking/step-1') !== -1; 28 29 // Only SynXis booking searches, which live at the root. Leaves other 30 // SynXis pages alone, e.g. the Manage Reservations /signin link. 31 if (!isAzds && url.pathname !== '/') return; 32 const property = isAzds 33 ? this.propertyFromPath(url, dolliProperties) 34 : this.propertyFromHotelCode(url.searchParams.get('hotel'), dolliProperties); 35 36 if (!property) return; 37 38 const params = isAzds ? this.parseAzdsParams(url) : this.parseSynxisParams(url); 39 if (!params.startDate) { 40 params.startDate = property.arrive; 41 params.endDate = property.depart; 42 } 43 44 link.href = '/book/' + encodeURIComponent(property.hotel_code) + '?' + new URLSearchParams(params).toString(); 45 }); 46 }, 47 48 propertyFromHotelCode: function (hotelCode, dolliProperties) { 49 if (!hotelCode) return null; 50 return Object.values(dolliProperties).find((property) => String(property.hotel_code) === hotelCode.trim()) ?? null; 51 }, 52 53 // AZDS links point at the property page itself (/{slug}/#/booking/step-1, 54 // or a page beneath it), so the slug is the first path segment that 55 // matches a Dolli property. Skips a language prefix such as /e
55s/. 56 propertyFromPath: function (url, dolliProperties) { 57 const slug = url.pathname.split('/').filter(Boolean).find((segment) => dolliProperties[segment]); 58 return slug ? dolliProperties[slug] : null; 59 }, 60 61 parseSynxisParams: function (url) { 62 const params = {}; 63 const room = url.searchParams.get('room'); 64 const promo = url.searchParams.get('promo'); 65 const arrive = url.searchParams.get('arrive'); 66 const depart = url.searchParams.get('depart'); 67 if (arrive) { 68 params.startDate = arrive; 69 params.endDate = this.fixEndDate(arrive, depart); 70 } 71 if (promo) params.promoCode = promo; 72 // SynXis prepends a tab char (%09) to room codes. 73 if (room) params.roomTypeCode = room.trim(); 74 return params; 75 }, 76 77 parseAzdsParams: function (url) { 78 const params = {}; 79 if (url.hash.indexOf('?') === -1) return params; 80 const hashParams = new URLSearchParams(url.hash.split('?')[1]); 81 const promo = hashParams.get('promo'); 82 const arrive = hashParams.get('arrive'); 83 const depart = hashParams.get('depart'); 84 if (arrive) { 85 params.startDate = this.azdsDateToIso(arrive); 86 params.endDate = this.fixEndDate(params.startDate, depart ? this.azdsDateToIso(depart) : null); 87 } 88 if (promo) params.promoCode = promo; 89 return params; 90 }, 91 92 // AZDS dates are m/d/Y; Dolli expects YYYY-MM-DD. 93 azdsDateToIso: function (dateStr) { 94 const parts = dateStr.split('/'); 95 if (parts.length !== 3) return dateStr; 96 return parts[2] + '-' + parts[0].padStart(2, '0') + '-' + parts[1].padStart(2, '0'); 97 }, 98 99 // Returns endDate if it is at least one day after startDate; otherwise returns startDate + 1 day. 100 // Both dates must be YYYY-MM-DD strings. endDate may be null/undefined. 101 fixEndDate: function (startDate, endDate) { 102 const s = startDate.split('-').map(Number); 103 const startObj = new Date(s[0], s[1] - 1, s[2]); 104 let useEnd = new Date(s[0], s[1] - 1, s[2] + 1); 105 106 if (endDate) { 107 const e = endDate.split('-').map(Number); 108 const endObj = new Date(e[0], e[1] - 1, e[2]); 109 if (endObj > startObj) useEnd = endObj; 110 } 111 112 const y = useEnd.getFullYear(); 113 const m = String(useEnd.getMonth() + 1).padStart(2, '0'); 114 const d = String(useEnd.getDate()).padStart(2, '0'); 115 return y + '-' + m + '-' + d; 116 } 117 }; 118 119})(Drupal, once); 120;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.