(function() { 'use strict'; let swiperInstances = {}; function loadSwiper() { return new Promise((resolve) => { // Check if Swiper is already loaded if (window.Swiper) { resolve(); return; } // Load Swiper CSS const swiperCSS = document.createElement('link'); swiperCSS.rel = 'stylesheet'; swiperCSS.href = 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.css'; document.head.appendChild(swiperCSS); // Load Swiper JS const swiperJS = document.createElement('script'); swiperJS.src = 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.js'; swiperJS.onload = () => resolve(); document.head.appendChild(swiperJS); }); } // Widget configuration const WIDGET_CONFIG = { apiBase: window.API_BASE || '/api', propertyId: window.PROPERTY_ID, theme: 'light' }; // Widget state let widgetData = { property: null, unitPlans: [], selectedPlan: null, filters: { bedrooms: 'all', priceRange: 'all', sortBy: 'price' } }; // Utility functions function createElement(tag, className, innerHTML) { const element = document.createElement(tag); if (className) element.className = className; if (innerHTML) element.innerHTML = innerHTML; return element; } function formatPrice(price) { if (!price) return 'Contact for pricing'; return `$${price.toLocaleString()}`; } function formatPriceRange(min, max) { if (!min && !max) return 'Contact for pricing'; if (min && max && min !== max) return `${formatPrice(min)} - ${formatPrice(max)}`; return `Starting at ${formatPrice(min || max)}`; } // API functions async function fetchPropertyData() { try { const response = await fetch(`${WIDGET_CONFIG.apiBase}/widget/property/${WIDGET_CONFIG.propertyId}`); if (!response.ok) throw new Error('Failed to fetch property data'); return await response.json(); } catch (error) { console.error('Error fetching property data:', error); return null; } } async function trackApplicationClick(planId) { try { await fetch(`${WIDGET_CONFIG.apiBase}/widget/track-application`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ property_id: WIDGET_CONFIG.propertyId, plan_id: planId, referral_source: 'widget' }) }); } catch (error) { console.error('Error tracking application click:', error); } } // Widget rendering functions function renderPropertyHeader(property) { const header = createElement('div', 'property-header'); // Banner (if enabled) if (property.banner_enabled && property.banner_text) { const banner = createElement('div', 'property-banner'); banner.style.backgroundColor = property.banner_background_color || '#28a745'; banner.style.color = property.banner_text_color || '#ffffff'; banner.innerHTML = `

${property.banner_text}

`; header.appendChild(banner); } // Property info const info = createElement('div', 'property-info'); info.innerHTML = `
${property.property_logo ? `` : ''}

${property.property_name}

${property.property_address}, ${property.property_city}, ${property.property_state} ${property.property_zip}
${property.property_phone ? `
${property.property_phone}
` : ''} ${property.property_email ? `
${property.property_email}
` : ''} ${property.property_website ? ` ` : ''}
${property.property_description ? `

${property.property_description}

` : ''} `; header.appendChild(info); return header; } function renderFilters() { const filters = createElement('div', 'widget-filters'); filters.innerHTML = `
`; // Add event listeners filters.querySelector('#bedrooms-filter').addEventListener('change', (e) => { widgetData.filters.bedrooms = e.target.value; renderUnitPlans(); }); filters.querySelector('#sort-filter').addEventListener('change', (e) => { widgetData.filters.sortBy = e.target.value; renderUnitPlans(); }); return filters; } function renderUnitPlan(plan) { const planElement = createElement('div', 'unit-plan'); const primaryImage = plan.media.find(m => m.is_featured); planElement.innerHTML = `
${primaryImage ? `${plan.plan_name}` : '
No Image Available
'}

${plan.plan_name}

${plan.bedrooms === 0 ? 'Studio' : `${plan.bedrooms} Bed`} ${plan.bathrooms} Bath ${plan.square_feet ? `${plan.square_feet} sq ft` : ''}
${formatPriceRange(plan.rent_range_min || plan.base_rent, plan.rent_range_max)}
${plan.availability_count > 0 ? `
${plan.availability_count} units available
` : '
Contact for availability
' } ${plan.availability_notes ? `
${plan.availability_notes}
` : ''}
`; return planElement; } function renderUnitPlans() { const container = document.querySelector('.unit-plans-container'); if (!container) return; // Filter and sort unit plans let filteredPlans = [...widgetData.unitPlans]; // Filter by bedrooms if (widgetData.filters.bedrooms !== 'all') { const bedroomCount = parseInt(widgetData.filters.bedrooms); filteredPlans = filteredPlans.filter(plan => { if (bedroomCount === 4) return plan.bedrooms >= 4; return plan.bedrooms === bedroomCount; }); } // Sort plans filteredPlans.sort((a, b) => { switch (widgetData.filters.sortBy) { case 'price': const priceA = a.rent_range_min || a.base_rent || 0; const priceB = b.rent_range_min || b.base_rent || 0; return priceA - priceB; case 'price-desc': const priceDescA = a.rent_range_max || a.base_rent || 0; const priceDescB = b.rent_range_max || b.base_rent || 0; return priceDescB - priceDescA; case 'bedrooms': return a.bedrooms - b.bedrooms; case 'sqft': return (b.square_feet || 0) - (a.square_feet || 0); default: return 0; } }); // Clear and render container.innerHTML = ''; filteredPlans.forEach(plan => { container.appendChild(renderUnitPlan(plan)); }); if (filteredPlans.length === 0) { container.innerHTML = '
No units match your criteria.
'; } } async function renderGalleryModal(plan) { // Load Swiper if not already loaded await loadSwiper(); const modalId = `gallery-modal-${plan.plan_id}`; const modal = createElement('div', 'plan-modal'); modal.innerHTML = ` `; document.body.appendChild(modal); // Initialize Swiper after modal is added to DOM if (plan.media.length > 0) { setTimeout(() => { // Initialize thumbnail swiper if more than 1 image let galleryThumbs = null; if (plan.media.length > 1) { galleryThumbs = new Swiper(`.gallery-thumbs-${plan.plan_id}`, { spaceBetween: 10, slidesPerView: 4, freeMode: true, watchSlidesProgress: true, breakpoints: { 320: { slidesPerView: 3, }, 640: { slidesPerView: 4, }, 768: { slidesPerView: 5, } } }); swiperInstances[`thumbs-${plan.plan_id}`] = galleryThumbs; } // Initialize main swiper const gallerySwiper = new Swiper(`.gallery-swiper-${plan.plan_id}`, { spaceBetween: 10, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, pagination: { el: '.swiper-pagination', clickable: true, }, thumbs: galleryThumbs ? { swiper: galleryThumbs, } : null, loop: plan.media.length > 1, keyboard: { enabled: true, } }); swiperInstances[`main-${plan.plan_id}`] = gallerySwiper; }, 100); } // Close modal when clicking outside modal.addEventListener('click', (e) => { if (e.target === modal) { closePlanModal(); } }); } async function renderPlanModal(plan) { await loadSwiper(); const modal = createElement('div', 'plan-modal'); modal.innerHTML = ` `; document.body.appendChild(modal); if (plan.media.length > 0) { setTimeout(() => { // Initialize thumbnail swiper if more than 1 image let detailThumbs = null; if (plan.media.length > 1) { detailThumbs = new Swiper(`.detail-thumbs-${plan.plan_id}`, { spaceBetween: 10, slidesPerView: 4, freeMode: true, watchSlidesProgress: true, breakpoints: { 320: { slidesPerView: 3 }, 640: { slidesPerView: 4 }, 768: { slidesPerView: 5 }, 1024: { slidesPerView: 6 } } }); swiperInstances[`detail-thumbs-${plan.plan_id}`] = detailThumbs; } const detailSwiper = new Swiper(`.detail-swiper-${plan.plan_id}`, { spaceBetween: 0, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, pagination: { el: '.swiper-pagination', clickable: true, dynamicBullets: true, }, thumbs: detailThumbs ? { swiper: detailThumbs, } : null, loop: plan.media.length > 1, keyboard: { enabled: true, }, effect: 'fade', fadeEffect: { crossFade: true } }); swiperInstances[`detail-main-${plan.plan_id}`] = detailSwiper; }, 100); } // Close modal when clicking outside modal.addEventListener('click', (e) => { if (e.target === modal) { closePlanModal(); } }); } // Global functions (attached to window for onclick handlers) window.showPlanDetails = function(planId) { const plan = widgetData.unitPlans.find(p => p.plan_id === planId); if (plan) { renderPlanModal(plan); } }; window.showGallery = function(planId) { const plan = widgetData.unitPlans.find(p => p.plan_id === planId); if (plan) { renderGalleryModal(plan); } }; window.closePlanModal = function() { const modal = document.querySelector('.plan-modal'); if (modal) { // Destroy Swiper instances to prevent memory leaks Object.keys(swiperInstances).forEach(key => { if (swiperInstances[key]) { swiperInstances[key].destroy(); delete swiperInstances[key]; } }); modal.remove(); } }; window.applyNow = function(planId) { const plan = widgetData.unitPlans.find(p => p.plan_id === planId); if (plan && widgetData.property) { // Track the application click trackApplicationClick(planId); // Redirect to application URL const applicationUrl = widgetData.property.default_application_url; if (applicationUrl) { window.open(applicationUrl, '_blank'); } else { alert('Please contact the property for application information.'); } } }; // Widget styles function injectStyles() { const primaryColor = widgetData.property?.widget_primary_color || '#007bff'; const secondaryColor = widgetData.property?.widget_secondary_color || '#6c757d'; const fontFamily = widgetData.property?.widget_font_family || 'Arial, sans-serif'; const styles = ` `; document.head.insertAdjacentHTML('beforeend', styles); } // Initialize widget async function initWidget() { const container = document.getElementById('widget-container'); if (!container) { console.error('Widget container not found'); return; } // Show loading container.innerHTML = '
Loading property information...
'; // Fetch data const data = await fetchPropertyData(); if (!data) { container.innerHTML = '
Failed to load property information. Please try again later.
'; return; } widgetData.property = data.property; widgetData.unitPlans = data.unitPlans; // Inject styles injectStyles(); // Render widget const widget = createElement('div', 'property-availability-widget'); // Property header widget.appendChild(renderPropertyHeader(widgetData.property)); // Filters widget.appendChild(renderFilters()); // Unit plans container const unitPlansContainer = createElement('div', 'unit-plans-container'); widget.appendChild(unitPlansContainer); // Replace container content container.innerHTML = ''; container.appendChild(widget); // Initial render of unit plans renderUnitPlans(); } // Start the widget when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initWidget); } else { initWidget(); } })();