1/** 2 * WBSL Interactive Map - Version 8 - NEW SVG FORMAT 3 * VANILLA JAVASCRIPT VERSION - NO JQUERY REQUIRED 4 * 5 * Updated to handle new SVG with UPPERCASE IDs and underscore format 6 * Maintains backward compatibility with old lowercase-hyphen format 7 * 8 * New SVG Format: STAGE_A, BUILDING_33, ZONE_10 9 * Old SVG Format: stage-a, building-33, zone-10 10 */ 11 12(function() { 13 'use strict'; 14 15 console.log('ð§ WBSL Map script loaded (Vanilla JS - No jQuery)'); 16 17 // ==================================================================== 18 // CONFIGURATION 19 // ==================================================================== 20 21 var CONFIG = { 22 // Building numbers that are WORKSHOPS 23 workshopBuildings: [4, 10, 24, 25, 33, 121, 120, 122], 24 25 // Building numbers that are OFFICES 26 officeBuildings: [1, 3, 7, 8, 9], 27 28 // Special cases 29 specialLocations: { 30 'LEAVESDEN_PARK': 'offices', 31 'LEAVESDEN-PARK': 'offices', 32 'R_SUITE': 'offices', 33 'R-SUITE': 'offices', 34 } 35 }; 36 37 // ==================================================================== 38 // HELPER FUNCTIONS 39 // ==================================================================== 40 41 function normalizeId(id) { 42 if (!id) return null; 43 var normalized = id.toLowerCase(); 44 normalized = normalized.replace(/_/g, '-'); 45 return normalized; 46 } 47 48 function getCategoryFromBuilding(id) { 49 var match = id.match(/building[-_]?(\d+)/i); 50 if (!match) return null; 51 52 var buildingNum = parseInt(match[1]); 53 54 if (CONFIG.workshopBuildings.indexOf(buildingNum) !== -1) { 55 return 'workshops'; 56 } 57 58 if (CONFIG.officeBuildings.indexOf(buildingNum) !== -1) { 59 return 'offices'; 60 } 61 62 return null; 63 } 64 65 function getCategoryFromStage(id) { 66 if (id.match(/stage[-_]?[a-m0-9]/i) || id.match(/[a-m0-9][-_]?stage/i)) { 67 return 'stages'; 68 } 69 return null; 70 } 71 72 function getCategoryFromZone(id) { 73 if (id.match(/zone[-_]?\d+/i)) { 74 return 'support'; 75 } 76 return null; 77 } 78 79 function getCategoryFromMill(id) { 80 if (id.match(/mill[-_]?[a-z]/i) || id.match(/[a-z][-_]?mill/i)) { 81 return 'stages'; 82 } 83 return null; 84 } 85 86 function getGroupCategory(id, acfData) { 87 if (!id) return null; 88 89 var normalizedId = normalizeId(id); 90 91 // 1. Check ACF data first 92 if (acfData && acfData.category) { 93 return acfData.category; 94 } 95 96 // 2. Check special locations 97 var specialKeys = Object.keys(CONFIG.specialLocations); 98 for (var i = 0; i < specialKeys.length; i++) { 99 if (specialKeys[i].toLowerCase() === normalizedId) { 100 return CONFIG.specialLocations[specialKeys[i]]; 101 } 102 } 103 104 // 3-6. Check various patterns 105 var buildingCategory = getCategoryFromBuilding(id); 106 if (buildingCategory) return buildingCategory; 107 108 var stageCategory = getCategoryFromStage(id); 109 if (stageCategory) return stageCategory; 110 111 var zoneCategory = getCategoryFromZone(id); 112 if (zoneCategory) return zoneCategory; 113 114 var millCategory = getCategoryFromMill(id); 115 if (millCategory) return millCategory; 116 117 return null; 118 } 119 120 function findSVGGroup(acfId) { 121 var svg = document.querySelector('#site-map-svg'); 122 if (!svg) return null; 123 124 var normalizedAcfId = normalizeId(acfId); 125 126 // Build comprehensive variations array 127 var variations = [ 128 acfId, // Original 129 normalizedAcfId, // Normalized 130 acfId.toUpperCase(), // UPPERCASE 131 acfId.toUpperCase().replace(/-/g, '_'), // UPPERCASE_UNDERSCORE 132 ]; 133 134 // Handle stages: a-stage â STAGE_A 135 var stageMatch = acfId.match(/^([a-z])-stage$/i); 136 if (stageMatch) { 137 var letter = stageMatch[1].toUpperCase(); 138 variations.push('STAGE_' + letter); 139 variations.push('STAGE-' + letter); 140 variations.push(letter + '-STAGE'); 141 variations.push(letter + '_STAGE'); 142 } 143 144 // Handle numbered stages: stage-1 â STAGE_1 145 var numberedStageMatch = acfId.match(/^stage-(\d+)$/i); 146 if (numberedStageMatch) { 147 var num = numberedStageMatch[1]; 148 variations.push('STAGE_' + num); 149 variations.push('STAGE-' + num); 150 } 151 152 // Handle buildings with suffixes: building-5-workshops â BUILDING_5 153 var buildingMatch = acfId.match(/^building-(\d+(?:-\d+)?)-/i); 154 if (buildingMatch) { 155 var buildingNum = buildingMatch[1]; 156 variations.push('BUILDING_' + buildingNum.replace(/-/g, '_')); 157 variations.push('BUILDING-' + buildingNum); 158 variations.push('BUILDING_' + buildingNum.replace(/-/g, '')); 159 } 160 161 // Handle simple buildings: building-1 â BUILDING_1 162 var simpleBuildingMatch = acfId.match(/^building-(\d+)$/i); 163 if (simpleBuildingMatch) { 164 variations.push('BUILDING_' + simpleBuildingMatch[1]); 165 variations.push('BUILDING-' + simpleBuildingMatch[1]); 166 } 167 168 // Handle zones: zone-1 â ZONE_1 169 var zoneMatch = acfId.match(/^zone-(\d+)$/i); 170 if (zoneMatch) { 171 var num = zoneMatch[1]; 172 variations.push('ZONE_' + num); 173 variations.push('ZONE-' + num); 174 } 175 176 // Handle special location patterns 177 if (acfId.includes('leavesden-park')) { 178 variations.push('LEAVESDEN_PARK'); 179 variations.push('LEAVESDEN-PARK'); 180 } 181 182 if (acfId.includes('multifunction')) { 183 variations.push('MULTIFUNCTION_SPACES'); 184 variations.push('MULTIFUNCTION-SPACES'); 185 } 186 187 if (acfId.includes('rigging-yard')) { 188 variations.push('WB_RIGGING_YARD'); 189 variations.push('RIGGING_YARD'); 190 } 191 192 if (acfId.includes('set-lighting-yard')) { 193 variations.push('WB_SET_LIGHTING_YARD'); 194 variations.push('SET_LIGHTING_YARD'); 195 } 196 197 if (acfId.includes('screening-rooms')) { 198 variations.push('SCREENING_ROOMS'); 199 variations.push('SCREENING-ROOMS'); 200 } 201 202 if (acfId.includes('support-spaces')) { 203 variations.push('SUPPORT_SPACES'); 204 variations.push('SUPPORT-SPACES'); 205 } 206 207 // Try each variation 208 for (var i = 0; i < variations.length; i++) { 209 var id = variations[i]; 210 211 // Try exact match 212 try { 213 var group = svg.querySelector('#' + CSS.escape(id)); 214 if (group) return group; 215 } catch(e) { 216 // CSS.escape might fail, continue 217 } 218 219 // Try case-insensitive match 220 var allGroups = svg.querySelectorAll('g[id]'); 221 for (var j = 0; j < allGroups.length; j++) { 222 if (allGroups[j].id.toLowerCase() === id.toLowerCase()) { 223 return allGroups[j]; 224 } 225 } 226 } 227 228 return null; 229 } 230 231 function getLocationName(id, acfData) { 232 if (acfData && acfData.name) { 233 return acfData.name; 234 } 235 236 var buildingMatch = id.match(/building[-_]?(\d+)/i); 237 if (buildingMatch) { 238 return 'Building ' + buildingMatch[1]; 239 } 240
241 var stageMatch = id.match(/stage[-_]?([a-m0-9])/i) || id.match(/([a-m0-9])[-_]?stage/i); 242 if (stageMatch) { 243 return 'Stage ' + stageMatch[1].toUpperCase(); 244 } 245 246 var zoneMatch = id.match(/zone[-_]?(\d+)/i); 247 if (zoneMatch) { 248 return 'Zone ' + zoneMatch[1]; 249 } 250 251 var millMatch = id.match(/mill[-_]?([a-z])/i) || id.match(/([a-z])[-_]?mill/i); 252 if (millMatch) { 253 return 'Mill ' + millMatch[1].toUpperCase(); 254 } 255 256 var normalizedId = normalizeId(id); 257 if (normalizedId === 'leavesden-park') return 'Leavesden Park'; 258 if (normalizedId === 'r-suite') return 'R Suite'; 259 260 return id.replace(/[-_]/g, ' ').split(' ').map(function(word) { 261 return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); 262 }).join(' '); 263 } 264 265 // ==================================================================== 266 // HIGHLIGHTING FUNCTIONS - CSS CLASS BASED 267 // ==================================================================== 268 269 function applyHighlight(group) { 270 if (!group) return; 271 // Simply add the 'highlighted' class - CSS handles the styling 272 group.classList.add('highlighted'); 273 } 274 275 function removeHighlight(group) { 276 if (!group) return; 277 // Remove the 'highlighted' class 278 group.classList.remove('highlighted'); 279 } 280 281 // ==================================================================== 282 // TOOLTIP HELPER 283 // ==================================================================== 284 285 function createTooltip() { 286 var tooltip = document.createElement('div'); 287 tooltip.className = 'map-tooltip'; 288 tooltip.style.position = 'absolute'; 289 tooltip.style.display = 'none'; 290 tooltip.style.padding = '8px 12px'; 291 tooltip.style.background = 'rgba(0, 0, 0, 0.8)'; 292 tooltip.style.color = 'white'; 293 tooltip.style.borderRadius = '4px'; 294 tooltip.style.fontSize = '14px'; 295 tooltip.style.pointerEvents = 'none'; 296 tooltip.style.zIndex = '10000'; 297 tooltip.style.transition = 'opacity 0.2s'; 298 document.body.appendChild(tooltip); 299 return tooltip; 300 } 301 302 function showTooltip(tooltip, text) { 303 tooltip.textContent = text; 304 tooltip.style.display = 'block'; 305 tooltip.style.opacity = '1'; 306 } 307 308 function hideTooltip(tooltip) { 309 tooltip.style.opacity = '0'; 310 setTimeout(function() { 311 tooltip.style.display = 'none'; 312 }, 200); 313 } 314 315 function moveTooltip(tooltip, e) { 316 tooltip.style.left = (e.pageX + 15) + 'px'; 317 tooltip.style.top = (e.pageY + 15) + 'px'; 318 } 319 320 // ==================================================================== 321 // MAIN INITIALIZATION 322 // ==================================================================== 323 324 function initMap() { 325 console.log('ðºï¸ Initializing WBSL Interactive Map v8 (NEW SVG FORMAT)...'); 326 327 var locations = window.wbslMapLocations || []; 328 console.log('ð Loaded locations:', locations.length); 329 330 if (!locations.length) { 331 console.warn('â ï¸ No location data found'); 332 return; 333 } 334 335 var currentUrl = window.location.pathname; 336 var activeLocation = null; 337 338 // Find location matching current URL 339 for (var i = 0; i < locations.length; i++) { 340 var loc = locations[i]; 341 if (loc.url) { 342 // Extract pathname from location URL 343 var urlPath = loc.url; 344 345 // If it's a full URL, extract just the path 346 if (urlPath.indexOf('http') === 0) { 347 try { 348 var urlObj = document.createElement('a'); 349 urlObj.href = urlPath; 350 urlPath = urlObj.pathname; 351 } catch(e) { 352 // Fallback: try to extract path manually 353 urlPath = urlPath.replace(/^https?:\/\/[^\/]+/, ''); 354 } 355 } 356 357 // Normalize paths (remove trailing slashes for comparison) 358 var normalizedCurrent = currentUrl.replace(/\/$/, ''); 359 var normalizedLocation = urlPath.replace(/\/$/, ''); 360 361 // Check if paths match 362 if (normalizedCurrent === normalizedLocation) { 363 activeLocation = loc; 364 break; 365 } 366 } 367 } 368 369 if (activeLocation) { 370 console.log('ð¯ Active location detected:', activeLocation.name || activeLocation.id); 371 } else { 372 console.log('ð No active location (main studios page or unmatched URL)'); 373 } 374 375 var svg = document.querySelector('#site-map-svg'); 376 if (!svg) { 377 console.error('â SVG element not found (#site-map-svg)'); 378 return; 379 } 380 381 // Select filter checkboxes by name and value 382 var filterButtons = { 383 stages: document.querySelector('input[name="filters[]"][value="stages"]'), 384 workshops: document.querySelector('input[name="filters[]"][value="workshops"]'),
385 offices: document.querySelector('input[name="filters[]"][value="offices"]'), 386 support: document.querySelector('input[name="filters[]"][value="support"]') 387 }; 388 389 var interactiveGroups = []; 390 391 // ================================================================ 392 // SETUP INTERACTIVE GROUPS 393 // ================================================================ 394 395 for (var i = 0; i < locations.length; i++) { 396 var location = locations[i]; 397 var group = findSVGGroup(location.id); 398 399 if (group) { 400 var category = getGroupCategory(location.id, location); 401 402 interactiveGroups.push({ 403 element: group, 404 data: location, 405 category: category 406 }); 407 408 group.style.cursor = 'pointer'; 409 group.classList.add('map-location'); 410 if (category) { 411 group.classList.add('category-' + category); 412 } 413 414 // Create tooltip for this group 415 var tooltip = createTooltip(); 416 417 // Use closure to capture variables 418 (function(grp, loc, tip, cat) { 419 grp.addEventListener('mouseenter', function(e) { 420 var name = getLocationName(loc.id, loc); 421 showTooltip(tip, name); 422 }); 423 424 grp.addEventListener('mousemove', function(e) { 425 moveTooltip(tip, e); 426 }); 427 428 grp.addEventListener('mouseleave', function() { 429 hideTooltip(tip); 430 }); 431 432 if (loc.url) { 433 grp.addEventListener('click', function(e) { 434 e.preventDefault(); 435 window.location.href = loc.url; 436 }); 437 } 438 })(group, location, tooltip, category); 439 440 // Highlight if active location 441 if (activeLocation && location.id === activeLocation.id && category) { 442 applyHighlight(group); 443 group.classList.add('active-location'); // Mark as active page 444 } 445 } 446 } 447 448 console.log('ð¯ Interactive groups configured:', interactiveGroups.length, 'locations'); 449 450 // ================================================================ 451 // FILTER FUNCTIONALITY 452 // ================================================================ 453 454 var activeFilters = { 455 stages: false, 456 workshops: false, 457 offices: false, 458 support: false 459 }; 460 461 function updateFilters() { 462 // Remove all filter highlights 463 for (var i = 0; i < interactiveGroups.length; i++) { 464 var item = interactiveGroups[i]; 465 if (!activeLocation || item.data.id !== activeLocation.id) { 466 removeHighlight(item.element); 467 } 468 } 469 470 var hasActiveFilters = activeFilters.stages || activeFilters.workshops || 471 activeFilters.offices || activeFilters.support; 472 473 if (hasActiveFilters) { 474 for (var i = 0; i < interactiveGroups.length; i++) { 475 var item = interactiveGroups[i]; 476 if (item.category && activeFilters[item.category]) { 477 if (!activeLocation || item.data.id !== activeLocation.id) { 478 applyHighlight(item.element); 479 } 480 } 481 } 482 } else if (activeLocation) { 483 for (var i = 0; i < interactiveGroups.length; i++) { 484 var item = interactiveGroups[i]; 485 if (item.data.id === activeLocation.id && item.category) { 486 applyHighlight(item.element); 487 break; 488 } 489 } 490 } 491 } 492 493 // Filter button event listeners 494 var filterKeys = Object.keys(filterButtons); 495 for (var i = 0; i < filterKeys.length; i++) { 496 (function(key) { 497 var button = filterButtons[key]; 498 if (button) { 499 // Force checkbox to be checked on load 500 button.checked = true; 501 activeFilters[key] = true; 502 503 button.addEventListener('change', function() { 504 activeFilters[key] = this.checked; 505 updateFilters(); 506 }); 507 } 508 })(filterKeys[i]); 509 } 510 511 console.log('ð¨ Filters configured'); 512 513 // Apply initial filter state (all checked - acts as legend) 514 updateFilters(); 515 516 // ================================================================ 517 // ZOOM CONTROLS 518 // ================================================================ 519 520 // Select zoom controls (check if they exist) 521 var zoomIn = document.getElementById('zoom-in'); 522 var zoomOut = document.getElementById('zoom-out'); 523 var zoomReset = document.getElementById('zoom-reset'); 524 525 // Only set up zoom if controls exist 526 if (zoomIn || zoomOut || zoomReset) { 527 528 var currentScale = 1; 529 var scaleStep = 0.2; 530 var minScale = 0.5; 531 var maxScale = 3; 532 533 function updateZoom() { 534 svg.style.transform = 'scale(' + currentScale + ')'; 535 svg.style.transformOrigin = 'center center'; 536 } 537 538 if (zoomIn) { 539 zoomIn.addEventListener('click', function() { 540 if (currentScale < maxScale) { 541 currentScale += scaleStep; 542 updateZoom(); 543 } 544 }); 545 } 546 547 if (zoomOut) { 548 zoomOut.addEventListener('click', function() { 549 if (currentScale > minScale) { 550 currentScale -= scaleStep; 551 updateZoom(); 552 } 553 }); 554 } 555 556 if (zoomReset) { 557 zoomReset.addEventListener('click', function() { 558 currentScale = 1; 559 updateZoom(); 560 }); 561 } 562 563 console.log('ð Zoom controls configured'); 564 } 565 566 console.log('â Map initialized successfully'); 567 } 568 569 // ==================================================================== 570 // WAIT FOR DOM READY 571 // ==================================================================== 572 573 if (document.readyState === 'loading') { 574 document.addEventListener('DOMContentLoaded', initMap); 575 } else { 576 initMap(); 577 } 578 579})();
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.