
RESERVE YOUR STAY IN PARADISE
INCLUDED SERVICES:
CHEF • BUTLER • HOUSEKEEPING • PERSONAL CONCIERGE
OUR SUMMER SALE IS ON!
Receive 20% off your entire stay.
Offer to be applied after booking confirmation.
*Offer valid on stays booked from June 15 - October 31, 2025
(function(){
const FEED_URL = "https://secure.365villas.com/vros/api/ical```d/MjM=?owner_token=36da7c8a0bba514508ca9886e4dd24aebdb27228.ics";
const PROXY = "https://api.allorigins.win/raw?```="+encodeURIComponent(FEED_URL);
// Utility for MM-DD-YYYY
function fmt(date){return dayjs(date).format('MM-DD-YYYY');}
// Load blackout/checkin/checkout sets```om iCal
async function loadS```(){
const cached = sessionStorage.getItem('villaDays');
if(cached) return JSON.parse(cached);
try {
const txt = await fetch(PROXY).then(r=>r.text());
const evs```[...txt.matchAll(/BEGIN:VEVENT[\s\S]+?END:VEVENT/g```
const blocked = new Set(), checkin = new Set(), checkout = new Set```
evs.forEach(ev=>{
const s = ev[0].match(/DTSTART(?:;VALUE=DATE)?:(\d{8})/);
const```= ev[0].match(/DTEND(?:;VALUE=```E)?:(\d{8})/);
if(s && e) {
const start = day```s[1], 'YYYYMMDD');
const end ```ayjs(e[1], 'YYYYMMDD'); // DTEND is exclusive
for```t d=start; d.isBefore(end); d=d.add(1,'day')){
if(d.isSame(start)) checkout.add(fmt(d));
else if(d.add(1,'day').isSame(end)) checkin.add(fmt(d));
```e blocked.add(fmt(d));
}
}
});
const```j = {blocked:[...blocked],checkin:[...checkin],checkout```..checkout]};
sessionStorage.setItem('villaDays```SON.stringify(obj));
return obj;
} catch(e) {
console.error```rror loading calendar ', e);
return {blocked:[],checkin:[],checkout:[]};
}
}
// Clear hover range highlighting
function clearHoverRange() {
$('.ui-datepicker t```.removeClass('ui-datepicker-hover-range');
}
// Highlight hover range from```rival to current hover date
function highlightHoverRange```rivalStr, hoverDateStr) {
clear```erRange();
if (!arrivalStr || !hoverDateStr) return;
try {
const fmtStr = "mm-dd-yy";
const arrivalDate = $.datepicker.parseDate(fmtStr, arr```lStr);
const hoverDate = $.datepicker.parse```e(fmtStr, hoverDateStr);
if (!arrivalDate || !hoverDate || hoverDate <=```rivalDate) return;
$('.ui-datepicker td').each(function(){
const $```= $(this);
const dateStr = $td.attr```ata-date');
if (!dateStr) return;
``` const cellDate = $.datepicker.parse```e(fmtStr, dateStr);
if (cellDate && cellDate > arrivalDate && cellDate <= hover```e) {
if (!$td.hasClass('ui-datepicker-unavailable')) {
$td.```Class('ui-datepicker-hover-range');
}
}
});
} catch(e) {
console```ror('Error highlighting hover range:', e);
}
}
// Enhanced range logic with hover preview```function markStayRange(arrivalStr, depart```Str, sets){
$('.ui-datepicker td').removeClass```i-datepicker-selected ui```tepicker-inrange range-start range-middle range-end');
markUnavailableRanges(sets);
if(!arrivalStr || !departureStr```eturn;
try {
const fmtStr = "mm-dd-yy";
const aDate = $.datepicker.parseDate(fmtStr, arr```lStr);
const dDate = $.datepicker.parseDate```tStr, departureStr);
if```Date || !dDate) return;
$('.ui-datepicker td').each(function(){
const $t``` $(this);
const dateStr = $td.attr('data-date');
if(!dateStr) return;
``` const cDate = $.datepicker.parseDate(fmtStr, date```);
if(!cDate) return;
if(+cDate === +aDate || +cDate === +dDate){
$td.addClass('ui-datepicker-selected');
} else if(cDate > aDate && cDate < dDate){
$td.```Class('ui-datepicker-inrange');
}
});
} catch(e) {
console.error```rror marking stay range:', e);
}
}
// Mark unavailable ranges with proper tailend styling
function markUnavailable```ges(sets) {
if(!sets || !sets.blocked) return;
try {
const unavailableDates = [...sets.blocked].sort();
const ranges```[];
let currentRange = [];
for (let i = 0; i < unav```ableDates.length; i++) {
const current```e = dayjs(unavailableDates[i], 'MM-DD-YYYY');
const nextDate = i```unavailableDates.length - 1 ? dayjs(unavailableDates[i + 1], 'MM-DD-YYYY') : null;```
currentRange.push(unavailableDates[i]);
if (!nextDate || !nextDate.isSame(currentDate.add(1, 'day'))) {
ranges.push([...currentRange]);
currentRange = [];
}
}
ranges.forEach(range => {
if```ange.length === 1) {
const dateStr = day```range[0], 'MM-DD-YYYY').format('MM-DD-YY');
const $td =````.ui-datepicker td[data-date="${dateStr}"]`);
$td.addClass```ange-start range-end');
} else {
```ge.forEach((date, index) => {
const dateStr```dayjs(date, 'MM-DD-YYYY').format('MM-DD-YY');
const $td = $(`.ui-datepicker td[data-date="${dateStr}"]`);
if```ndex === 0) {
$td.addClass('range-start');
} else if (index === range.length - 1) {
$td.addClass('range-end');
} else {
$td.addClass('range-middle');
}
});
}
});
} catch(e) {
console.error('Error marking unavailable ranges:', e);
}
}
// Datepicker buil```ith enhanced functionality
function build(sets){
const BLK = new Set(sets.blocked ||```);
const IN = new Set(sets.checkin``` []);
const OUT = new Set(sets.checkout || []);
function classify(date){
const key = fmt(date);
if(BLK.has(key)) return [false,'ui-datepicker-unavailable','Not Available'];
if(IN.has(key)) return [true ,'ui-datepicker-checkin','Available for Check-In'];
if(OUT.has(key)) return [true ,'ui-datepicker-checkout','Available for Check-Out'];
return [true ,'','Available'];
}
function injectL```nd(){
setTimeout(function(){
if(document.querySelector('.ui-datepicker-legend')) return;
const box = document.createElement('div');
box.className='ui-datepicker-legend';``` box.innerHTML=`
```v class="ui-datepicker-legend-item">
```v class="ui-datepicker-legend-box legend-in">
```v class="ui-datepicker-legend-item">
```v class="ui-datepicker-legend-box legend-out">X
``` Available
✓
Selecte```div>
A
```ilable
Available For```>Check-In
Available For
Check-Out
Check-Out