// Application State let currentFilter = 'all'; let currentSort = 'featured'; let currentView = 'grid'; let currentSearchTerm = ''; let filteredLocations = [...LOCATIONS_DATA]; let map = null; let markers = []; let currentPage = 1; let itemsPerPage = 6; let activeFilters = []; // Initialize the application document.addEventListener('DOMContentLoaded', function() { initializeMap(); renderLocations(); updateResultsCount(); setupSearch(); updatePagination(); showMobileMapToggle(); }); // Show mobile map toggle on smaller screens function showMobileMapToggle() { const toggle = document.getElementById('mobile-map-toggle'); if (window.innerWidth <= 1024) { toggle.style.display = 'flex'; } else { toggle.style.display = 'none'; } } // Handle window resize window.addEventListener('resize', showMobileMapToggle); // Toggle mobile map function toggleMobileMap() { const mapContainer = document.getElementById('map-container'); const isVisible = mapContainer.style.display !== 'none'; if (isVisible) { mapContainer.style.display = 'none'; document.getElementById('mobile-map-toggle').innerHTML = ` Show Map `; } else { mapContainer.style.display = 'block'; document.getElementById('mobile-map-toggle').innerHTML = ` Show Grid `; } } // Initialize Mapbox map function initializeMap() { try { // Use demo token for development mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw'; map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/light-v11', center: [-2.5, 54.0], // Center of UK zoom: 5.5 }); map.on('load', function() { addMarkersToMap(); }); // Add navigation controls map.addControl(new mapboxgl.NavigationControl(), 'top-right'); } catch (error) { console.warn('Mapbox failed to initialize:', error); // Fallback: show message in map container document.getElementById('map').innerHTML = `

Interactive Map

Explore our premium workspace locations across the UK

`; } } // Add markers to map function addMarkersToMap() { if (!map) return; // Clear existing markers markers.forEach(marker => marker.remove()); markers = []; const paginatedLocations = getPaginatedLocations(); paginatedLocations.forEach(location => { // Create custom marker const el = document.createElement('div'); el.className = 'marker'; el.style.width = '12px'; el.style.height = '12px'; el.style.borderRadius = '50%'; el.style.backgroundColor = '#000'; el.style.border = '2px solid #fff'; el.style.cursor = 'pointer'; el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)'; // Create popup const popup = new mapboxgl.Popup({ offset: 25 }) .setHTML(` `); // Add marker to map const marker = new mapboxgl.Marker(el) .setLngLat(location.coordinates) .setPopup(popup) .addTo(map); markers.push(marker); }); } // Update active filters display function updateActiveFilters() { const container = document.getElementById('active-filters'); const filters = []; if (currentSearchTerm) { filters.push({ type: 'search', value: currentSearchTerm, label: `Search: "${currentSearchTerm}"` }); } if (currentFilter !== 'all') { const filterLabels = { 'available': 'Available Now', 'premium': 'Premium', 'coming': 'Coming Soon' }; filters.push({ type: 'filter', value: currentFilter, label: filterLabels[currentFilter] }); } if (filters.length === 0) { container.innerHTML = ''; return; } container.innerHTML = filters.map(filter => `
${filter.label}
`).join(''); } // Remove specific filter function removeFilter(type, value) { if (type === 'search') { document.getElementById('location-search').value = ''; currentSearchTerm = ''; } else if (type === 'filter') { currentFilter = 'all'; document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); document.querySelector('[data-filter="all"]').classList.add('active'); document.querySelector('[data-filter="all"]').setAttribute('aria-selected', 'true'); } applyFilters(); } // Apply all filters function applyFilters() { let filtered = [...LOCATIONS_DATA]; // Apply search filter if (currentSearchTerm) { filtered = filtered.filter(location => location.name.toLowerCase().includes(currentSearchTerm.toLowerCase()) || location.city.toLowerCase().includes(currentSearchTerm.toLowerCase()) || location.address.toLowerCase().includes(currentSearchTerm.toLowerCase()) || location.description.toLowerCase().includes(currentSearchTerm.toLowerCase()) ); } // Apply category filter if (currentFilter !== 'all') { filtered = filtered.filter(location => { if (currentFilter === 'available') { return location.badges.some(badge => badge.toLowerCase().includes('available')); } else if (currentFilter === 'premium') { return location.badges.some(badge => badge.toLowerCase().includes('premium')); } else if (currentFilter === 'coming') { return location.badges.some(badge => badge.toLowerCase().includes('coming')); } return location.tags.includes(currentFilter); }); } filteredLocations = filtered; currentPage = 1; // Reset to first page sortLocations(currentSort); renderLocations(); updateResultsCount(); updateActiveFilters(); updatePagination(); addMarkersToMap(); announceToScreenReader(`Filtered to ${filteredLocations.length} locations`); } // Filter locations function filterLocations(filter) { currentFilter = filter; // Update filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); const activeBtn = document.querySelector(`[data-filter="${filter}"]`); if (activeBtn) { activeBtn.classList.add('active'); activeBtn.setAttribute('aria-selected', 'true'); } applyFilters(); } // Sort locations function sortLocations(sortBy) { currentSort = sortBy; filteredLocations.sort((a, b) => { switch (sortBy) { case 'price-low': return a.pricing.amount - b.pricing.amount; case 'price-high': return b.pricing.amount - a.pricing.amount; case 'newest': return new Date(b.openingDate || '2020-01-01') - new Date(a.openingDate || '2020-01-01'); case 'popular': return (b.rating?.reviews || 0) - (a.rating?.reviews || 0); case 'featured': default: return (b.featured ? 1 : 0) - (a.featured ? 1 : 0); } }); renderLocations(); updatePagination(); addMarkersToMap(); } // Set view mode function setView(view) { currentView = view; document.querySelectorAll('.view-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelector(`[data-view="${view}"]`).classList.add('active'); const grid = document.getElementById('locations-grid'); const mapContainer = document.getElementById('map-container'); if (view === 'map') { // Show only map on mobile/tablet if (window.innerWidth <= 1024) { grid.style.display = 'none'; mapContainer.style.display = 'block'; mapContainer.style.position = 'relative'; mapContainer.style.height = '70vh'; mapContainer.style.top = '0'; } } else { // Show grid/list view grid.style.display = 'grid'; if (window.innerWidth <= 1024) { mapContainer.style.display = 'none'; } if (view === 'list') { grid.style.gridTemplateColumns = '1fr'; grid.style.gap = '16px'; // Modify cards for list view document.querySelectorAll('.location-card').forEach(card => { card.style.display = 'flex'; card.style.height = 'auto'; const image = card.querySelector('.location-image'); if (image) { image.style.width = '200px'; image.style.flexShrink = '0'; } }); } else { grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(350px, 1fr))'; grid.style.gap = '24px'; // Reset cards for grid view document.querySelectorAll('.location-card').forEach(card => { card.style.display = 'block'; card.style.height = 'auto'; const image = card.querySelector('.location-image'); if (image) { image.style.width = '100%'; image.style.flexShrink = 'initial'; } }); } } announceToScreenReader(`View changed to ${view}`); } // Search locations function searchLocations() { currentSearchTerm = document.getElementById('location-search').value.trim(); applyFilters(); } // Setup search functionality function setupSearch() { const searchInput = document.getElementById('location-search'); if (searchInput) { searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { searchLocations(); } }); // Real-time search with debounce let searchTimeout; searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { currentSearchTerm = this.value.trim(); applyFilters(); }, 300); }); } } // Clear all filters function clearFilters() { currentFilter = 'all'; currentSearchTerm = ''; document.getElementById('location-search').value = ''; document.getElementById('sort-locations').value = 'featured'; document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); document.querySelector('[data-filter="all"]').classList.add('active'); document.querySelector('[data-filter="all"]').setAttribute('aria-selected', 'true'); applyFilters(); announceToScreenReader('Filters cleared, showing all locations'); } // Pagination function getPaginatedLocations() { const startIndex Locations - Nest & Network | Premium Workspace Solutions

Premium workspace locations across the UK

From London's financial district to Manchester's innovation quarter, discover enterprise-grade workspace solutions in the UK's most prestigious business locations.

Our workspace locations

Enterprise-grade workspace solutions designed for ambitious teams and Fortune 500 companies.

6 locations available
// Initialize Mapbox map function initializeMap() { try { map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/light-v11', center: [-2.5, 54.0], // Center of UK zoom: 5.5 }); map.on('load', function() { addMarkersToMap(); }); // Add navigation controls map.addControl(new mapboxgl.NavigationControl(), 'top-right'); } catch (error) { console.warn('Mapbox failed to initialize:', error); // Fallback: show message in map container document.getElementById('map').innerHTML = `

Interactive Map

Explore our premium workspace locations across the UK

`; } } // Add markers to map function addMarkersToMap() { if (!map) return; // Clear existing markers markers.forEach(marker => marker.remove()); markers = []; filteredLocations.forEach(location => { // Create custom marker const el = document.createElement('div'); el.className = 'marker'; el.style.width = '12px'; el.style.height = '12px'; el.style.borderRadius = '50%'; el.style.backgroundColor = '#000'; el.style.border = '2px solid #fff'; el.style.cursor = 'pointer'; el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)'; // Create popup const popup = new mapboxgl.Popup({ offset: 25 }) .setHTML(` `); // Add marker to map const marker = new mapboxgl.Marker(el) .setLngLat(location.coordinates) .setPopup(popup) .addTo(map); markers.push(marker); }); } // Filter locations function filterLocations(filter) { currentFilter = filter; // Update filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); const activeBtn = document.querySelector(`[data-filter="${filter}"]`); if (activeBtn) { activeBtn.classList.add('active'); activeBtn.setAttribute('aria-selected', 'true'); } // Apply filter if (filter === 'all') { filteredLocations = [...LOCATIONS_DATA]; } else { filteredLocations = LOCATIONS_DATA.filter(location => { if (filter === 'available') { return location.badges.some(badge => badge.toLowerCase().includes('available')); } else if (filter === 'premium') { return location.badges.some(badge => badge.toLowerCase().includes('premium')); } else if (filter === 'coming') { return location.badges.some(badge => badge.toLowerCase().includes('coming')); } return location.tags.includes(filter); }); } sortLocations(currentSort); renderLocations(); updateResultsCount(); addMarkersToMap(); announceToScreenReader(`Filtered to ${filteredLocations.length} locations`); } // Sort locations function sortLocations(sortBy) { currentSort = sortBy; filteredLocations.sort((a, b) => { switch (sortBy) { case 'price-low': return a.pricing.amount - b.pricing.amount; case 'price-high': return b.pricing.amount - a.pricing.amount; case 'newest': return new Date(b.openingDate || '2020-01-01') - new Date(a.openingDate || '2020-01-01'); case 'popular': return (b.rating?.reviews || 0) - (a.rating?.reviews || 0); case 'featured': default: return (b.featured ? 1 : 0) - (a.featured ? 1 : 0); } }); renderLocations(); } // Set view mode function setView(view) { currentView = view; document.querySelectorAll('.view-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelector(`[data-view="${view}"]`).classList.add('active'); const grid = document.getElementById('locations-grid'); if (view === 'list') { grid.style.gridTemplateColumns = '1fr'; grid.style.gap = '16px'; // Modify cards for list view document.querySelectorAll('.location-card').forEach(card => { card.style.display = 'flex'; card.style.height = 'auto'; const image = card.querySelector('.location-image'); if (image) { image.style.width = '200px'; image.style.flexShrink = '0'; } }); } else { grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(350px, 1fr))'; grid.style.gap = '24px'; // Reset cards for grid view document.querySelectorAll('.location-card').forEach(card => { card.style.display = 'block'; card.style.height = 'auto'; const image = card.querySelector('.location-image'); if (image) { image.style.width = '100%'; image.style.flexShrink = 'initial'; } }); } announceToScreenReader(`View changed to ${view}`); } // Search locations function searchLocations() { const searchTerm = document.getElementById('location-search').value.toLowerCase().trim(); if (!searchTerm) { filteredLocations = [...LOCATIONS_DATA]; } else { filteredLocations = LOCATIONS_DATA.filter(location => location.name.toLowerCase().includes(searchTerm) || location.city.toLowerCase().includes(searchTerm) || location.address.toLowerCase().includes(searchTerm) || location.description.toLowerCase().includes(searchTerm) ); } renderLocations(); updateResultsCount(); addMarkersToMap(); announceToScreenReader(`Search returned ${filteredLocations.length} locations`); } // Setup search functionality function setupSearch() { const searchInput = document.getElementById('location-search'); if (searchInput) { searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { searchLocations(); } }); // Real-time search with debounce let searchTimeout; searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(searchLocations, 300); }); } } // Clear all filters function clearFilters() { currentFilter = 'all'; document.getElementById('location-search').value = ''; document.getElementById('sort-locations').value = 'featured'; document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); document.querySelector('[data-filter="all"]').classList.add('active'); document.querySelector('[data-filter="all"]').setAttribute('aria-selected', 'true'); filteredLocations = [...LOCATIONS_DATA]; sortLocations('featured'); renderLocations(); updateResultsCount(); addMarkersToMap(); announceToScreenReader('Filters cleared, showing all locations'); } // Render locations function renderLocations() { const grid = document.getElementById('locations-grid'); const emptyState = document.getElementById('empty-state'); if (filteredLocations.length === 0) { grid.classList.add('hidden'); emptyState.classList.remove('hidden'); return; } grid.classList.remove('hidden'); emptyState.classList.add('hidden'); grid.innerHTML = filteredLocations.map(location => createLocationCard(location)).join(''); // Setup card click handlers setupCardHandlers(); } // Create location card HTML function createLocationCard(location) { const badges = location.badges.map(badge => { const badgeClass = badge.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''); return `${badge}`; }).join(''); const rating = location.rating ? `
★★★★★ ${location.rating.score} (${location.rating.reviews})
` : `
Opening Soon
`; const amenities = location.amenities.map(amenity => `
${amenity}
` ).join(''); return `
${badges}

${location.name}

${rating}
${location.address}

${location.description}

${amenities}
`; } // Setup card click handlers function setupCardHandlers() { document.querySelectorAll('.location-card').forEach(card => { card.addEventListener('click', function(e) { if (!e.target.closest('.btn')) { const locationId = this.dataset.locationId; window.location.href = `/locations/${locationId}`; } }); card.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (!e.target.closest('.btn')) { const locationId = this.dataset.locationId; window.location.href = `/locations/${locationId}`; } } }); }); } // Update results count function updateResultsCount() { const count = filteredLocations.length; const resultsText = `${count} location${count !== 1 ? 's' : ''} available`; document.getElementById('results-count').textContent = resultsText; } // Announce to screen readers function announceToScreenReader(message) { const liveRegion = document.getElementById('live-region'); if (liveRegion) { liveRegion.textContent = message; setTimeout(() => liveRegion.textContent = '', 1000); } }