PageSourceSearch

https://www.wbsl.com/rigging/wp-content/themes/wbsl/assets/js/wbsl-map.js?ver=1771451538

js wbsl.com collected 2026-10-02 06:31:29 UTC 20,663 bytes, 579 lines download raw bytes

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.