1(function(window, document){ 2 'use strict'; 3 const NS = window.AUDSnazzyGoogleMaps || {}; 4 let pmtilesRegistered = false; 5 6 function showStatus(el, msg) { 7 const status = el.closest('.aud-sgm-shell')?.querySelector('.aud-sgm-status'); 8 if (!status) return; 9 if (msg) { status.textContent = msg; status.hidden = false; } 10 else { status.hidden = true; status.textContent = ''; } 11 } 12 13 function hex(v, fallback) { return /^#[0-9a-f]{6}$/i.test(v || '') ? v : fallback; } 14 15 function validLatLng(lat, lng) { 16 if (lat === '' || lng === '' || lat === null || lng === null || typeof lat === 'undefined' || typeof lng === 'undefined') return false; 17 const a=Number(lat), b=Number(lng); 18 return Number.isFinite(a) && Number.isFinite(b) && a>=-90 && a<=90 && b>=-180 && b<=180; 19 } 20 21 function clampPercent(value, fallback) { 22 const n=Number(value); return Number.isFinite(n) ? Math.max(0,Math.min(100,n)) : fallback; 23 } 24 25 function activeFocalPoint(cfg) { 26 const fp=cfg?.focalPoint||{}, d=fp.desktop||{}, t=fp.tablet||{}, m=fp.mobile||{}; 27 let c=d; if(window.innerWidth<=767)c=m; else if(window.innerWidth<=1024)c=t; 28 const fx=clampPercent(d.x,50), fy=clampPercent(d.y,50); 29 return {x:clampPercent(c.x,fx),y:clampPercent(c.y,fy)}; 30 } 31 32 function hasNumber(value){ 33 return value !== null && value !== '' && typeof value !== 'undefined' && Number.isFinite(Number(value)); 34 } 35 36 function activeZoom(cfg) { 37 const desktop=hasNumber(cfg.zoom)?Number(cfg.zoom):14; 38 if(window.innerWidth<=767 && hasNumber(cfg.mobileZoom))return Number(cfg.mobileZoom); 39 if(window.innerWidth<=1024 && hasNumber(cfg.tabletZoom))return Number(cfg.tabletZoom); 40 return desktop; 41 } 42 43 function isTouchLike(){return !!(window.matchMedia&&window.matchMedia('(hover: none), (pointer: coarse)').matches);} 44 45 function mapActionUrl(action,pos){ 46 if(!pos)return ''; 47 const lat=Array.isArray(pos)?Number(pos[1]):Number(pos.lat), lng=Array.isArray(pos)?Number(pos[0]):Number(pos.lng); 48 if(!Number.isFinite(lat)||!Number.isFinite(lng))return ''; 49 const d=encodeURIComponent(lat+','+lng); 50 if(action==='directions')return 'https://www.google.com/maps/dir/?api=1&destination='+d; 51 if(action==='google_maps')return 'https://www.google.com/maps/search/?api=1&query='+d; 52 return ''; 53 } 54 55 function popupHtml(item){ 56 const div=document.createElement('div'); div.className='aud-sgm-popup'; 57 if(item.title){const s=document.createElement('strong');s.textContent=item.title;div.appendChild(s);} 58 if(item.description){const p=document.createElement('p');p.innerHTML=item.description;div.appendChild(p);} 59 if(item.link){const a=document.createElement('a');a.href=item.link;a.textContent='View';if(item.newTab){a.target='_blank';a.rel='noopener';}div.appendChild(a);} 60 return div; 61 } 62 63 function primaryActionCard(item,pos){ 64 const div=document.createElement('div');div.className='aud-sgm-action-card'; 65 const title=document.createElement('strong');title.textContent=item.title||'Location';div.appendChild(title); 66 const actions=document.createElement('div');actions.className='aud-sgm-action-buttons'; 67 [['Open in Google Maps','google_maps'],['Get Directions','directions']].forEach(([label,action])=>{ 68 const url=mapActionUrl(action,pos); if(!url)return; 69 const a=document.createElement('a');a.href=url;a.target='_blank';a.rel='noopener';a.textContent=label;actions.appendChild(a); 70 }); 71 div.appendChild(actions);return div; 72 } 73 74 async function geocode(address){ 75 if(!NS.ajaxUrl)throw new Error('Geocoder unavailable.'); 76 const u=new URL(NS.ajaxUrl,window.location.href);u.searchParams.set('action','aud_sgm_geocode');u.searchParams.set('nonce',NS.geocodeNonce||'');u.searchParams.set('q',address); 77 const r=await fetch(u.toString(),{credentials:'same-origin'}); if(!r.ok)throw new Error('Geocode '+r.status); 78 const j=await r.json(); if(!j||!j.success||!j.data)throw new Error(j?.data?.message||'Location not found.'); 79 return [Number(j.data.lng),Number(j.data.lat)]; 80 } 81 82 function registerPMTiles(){ 83 if(pmtilesRegistered||!window.maplibregl||!window.pmtiles)return; 84 try{const protocol=new window.pmtiles.Protocol();window.maplibregl.addProtocol('pmtiles',protocol.tile);pmtilesRegistered=true;}catch(e){console.warn('[A+UD Map] PMTiles protocol registration failed',e);} 85 } 86 87 function googleLikePalette(cfg){ 88 const c=cfg.colors||{}; 89 return { 90 geometry:hex(c.geometry,'#f2f2f2'), water:hex(c.water,'#ffffff'), road:hex(c.road,'#ffffff'), 91 roadStroke:hex(c.roadStroke,'#b9b9b9'), highway:hex(c.highway,'#555555'), par
91k:hex(c.park,'#dddddd'), 92 poi:hex(c.poi,'#dddddd'), transit:hex(c.transit,'#aaaaaa'), administrative:hex(c.administrative,'#bdbdbd'), 93 label:hex(c.label,'#909090'), labelHalo:hex(c.labelHalo,'#f6f6f6') 94 }; 95 } 96 97 function builtInPMTilesStyle(cfg){ 98 const c=googleLikePalette(cfg), url=String(NS.pmtilesUrl||'').trim(); 99 const src={type:'vector',url:'pmtiles://'+url,attribution:'© OpenStreetMap contributors'}; 100 const roadWidth=(a,b,c1,d)=>['interpolate',['linear'],['zoom'],8,a,11,b,14,c1,17,d]; 101 return {version:8,glyphs:'https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf',sources:{pr:src},layers:[ 102 {id:'background',type:'background',paint:{'background-color':c.geometry}}, 103 {id:'landcover',type:'fill',source:'pr','source-layer':'landcover',paint:{'fill-color':c.geometry,'fill-opacity':0.7}}, 104 {id:'park',type:'fill',source:'pr','source-layer':'landuse',filter:['in',['get','class'],['literal',['park','cemetery','grass','wood','forest','recreation_ground']]],paint:{'fill-color':c.park,'fill-opacity':0.78}}, 105 {id:'water',type:'fill',source:'pr','source-layer':'water',paint:{'fill-color':c.water}}, 106 {id:'boundary',type:'line',source:'pr','source-layer':'boundary',paint:{'line-color':c.administrative,'line-width':['interpolate',['linear'],['zoom'],5,0.35,12,0.8,16,1.0],'line-opacity':0.7}}, 107 {id:'road-minor-casing',type:'line',source:'pr','source-layer':'transportation',filter:['in',['get','class'],['literal',['minor','service','track','path']]],paint:{'line-color':c.roadStroke,'line-width':roadWidth(0.25,0.5,1.15,2.3)}}, 108 {id:'road-minor',type:'line',source:'pr','source-layer':'transportation',filter:['in',['get','class'],['literal',['minor','service','track','path']]],paint:{'line-color':c.road,'line-width':roadWidth(0.05,0.2,0.7,1.65)}}, 109 {id:'road-secondary-casing',type:'line',source:'pr','source-layer':'transportation',filter:['in',['get','class'],['literal',['tertiary','secondary']]],paint:{'line-color':c.roadStroke,'line-width':roadWidth(0.45,0.9,1.8,3.4)}}, 110 {id:'road-secondary',type:'line',source:'pr','source-layer':'transportation',filter:['in',['get','class'],['literal',['tertiary','secondary']]],paint:{'line-color':c.road,'line-width':roadWidth(0.15,0.55,1.25,2.55)}}, 111 {id:'road-primary',type:'line',source:'pr','source-layer':'transportation',filter:['in',['get','class'],['literal',['primary','trunk','motorway']]],paint:{'line-color':c.highway,'line-width':roadWidth(0.8,1.45,2.4,4.6)}}, 112 {id:'road-labels',type:'symbol',source:'pr','source-layer':'transportation_name',minzoom:10,layout:{'symbol-placement':'line','text-field':['coalesce',['get','name:en'],['get','name']],'text-font':['Noto Sans Regular'],'text-size':['interpolate',['linear'],['zoom'],10,9,14,10,18,11],'text-keep-upright':true,'text-letter-spacing':0.01},paint:{'text-color':c.label,'text-halo-color':c.labelHalo,'text-halo-width':1.25}}, 113 {id:'place-labels',type:'symbol',source:'pr','source-layer':'place',minzoom:7,layout:{'text-field':['coalesce',['get','name:en'],['get','name']],'text-font':['Noto Sans Regular'],'text-size':['interpolate',['linear'],['zoom'],7,9.5,12,10.5,16,11.5]},paint:{'text-color':c.label,'text-halo-color':c.labelHalo,'text-halo-width':1.2}} 114 ]}; 115 } 116 117 function parseLegacyStyles(cfg){ 118 if(cfg.styleMode!=='json')return []; 119 let raw=String(cfg.customJson||'').trim();if(!raw)return []; 120 const a=raw.indexOf('['),b=raw.lastIndexOf(']');if(a>=0&&b>a)raw=raw.slice(a,b+1); 121 try{const x=JSON.parse(raw);return Array.isArray(x)?x:[];}catch(e){return [];} 122 } 123 124 function layerGroup(layer){ 125 const id=String(layer.id||'').toLowerCase(), sl=String(layer['source-layer']||'').toLowerCase(); 126 if(layer.type==='background')return 'landscape'; 127 if(sl==='building'||id.includes('building'))return 'building'; 128 if(sl==='water'||id.includes('water'))return 'water'; 129 if(sl==='transportation_name'||id.includes('road-label')||id.includes('road_label'))return 'roadLabels'; 130 // OpenMapTiles also contains polygonal transportation surfaces (large paved 131 // areas, platforms, apron-like shapes, etc.). Google does not render those as 132 // giant road-colored blobs in this stripped monochrome style, so keep them 133 // separate from actual road centerlines. 134 if(sl==='transportation'&&layer.type==='fill')return 'transportationArea'; 135 if(sl==='transportation'||id.includes('road')||id.includes('highway')||id.includes('street')||id.includes('motorway'))return 'road'; 136 if(sl==='poi'||id.includes('poi')||id.includes('business'))return 'poi'; 137 if(sl==='boundary'||id.includes('boundary')||id.includes('admin'))return 'administrative'; 138 if(sl==='transit'||id.includes('transit')||id.includes('rail')||id.includes('station'))return 'transit'; 139 if(/park|grass|forest|wood|cemetery|recreation|garden|nature-reserve|nature_reserve|meadow/.test(id))return 'park'; 140 if(sl==='landuse'||sl==='landcover'||id.includes('landcover')||id.includes('landuse'))return 'landscape'; 141 if(layer.type==='symbol')return 'labels'; 142 return 'other'; 143 } 144
145 function setLayoutSafe(map,id,prop,val){try{map.setLayoutProperty(id,prop,val);}catch(e){}} 146 function setPaintSafe(map,id,prop,val){try{map.setPaintProperty(id,prop,val);}catch(e){}} 147 148 function roadKind(layerOrId){ 149 const id=String(typeof layerOrId==='string'?layerOrId:(layerOrId?.id||'')).toLowerCase(); 150 if(/motorway|freeway/.test(id))return 'motorway'; 151 if(/trunk[_-]?primary|trunk|primary/.test(id))return 'primary'; 152 if(/secondary[_-]?tertiary|secondary|tertiary/.test(id))return 'secondary'; 153 if(/path|pedestrian|steps|service|track/.test(id))return 'path'; 154 return 'minor'; 155 } 156 157 // Calibrated against the user's original monochrome Google/Snazzy reference. 158 // Width is driven by each feature's OpenMapTiles class, not only by the layer id. 159 // This keeps service/track/path geometry from inheriting arterial weights inside 160 // mixed transportation layers. 161 function zoomWidth(v){ 162 // Ground-figure / near-physical scaling. In a Web-Mercator map the same 163 // real-world width occupies about twice as many pixels every +1 zoom. 164 // Using one-zoom stops therefore lets a 8â12 m local street read as an 165 // actual void between lots when the user zooms all the way in, instead of 166 // remaining a CAD hairline. Values are intentionally capped at z20 so the 167 // map stays usable beyond the architectural-reading range. 168 return ['interpolate',['linear'],['zoom'], 169 12,v[0],13,v[1],14,v[2],15,v[3],16,v[4],17,v[5],18,v[6],19,v[7],20,v[8] 170 ]; 171 } 172 173 function roadWidthExpr(kind,casing){ 174 const m={ 175 // z12 ... z20. Approximate visual ground widths calibrated for San Juan. 176 // Local streets approach an 8â10 m apparent width at close zoom; larger 177 // classes remain proportionally wider. Paths/service access stay thin. 178 path: [0.10,0.14,0.20,0.32,0.52,0.85,1.35,2.10,3.20], 179 minor: [0.32,0.52,0.88,1.55,2.85,5.10,9.20,16.5,29.0], 180 secondary:[0.52,0.82,1.35,2.35,4.25,7.55,13.6,24.0,41.0], 181 primary: [0.82,1.25,2.05,3.55,6.30,11.0,19.5,34.0,56.0], 182 motorway: [1.20,1.85,3.05,5.25,9.20,16.0,28.0,48.0,76.0] 183 }; 184 const add={path:.08,minor:.16,secondary:.22,primary:.30,motorway:.40}[kind]||.16; 185 const v=(m[kind]||m.minor).map(x=>x+(casing?add:0)); 186 return zoomWidth(v); 187 } 188 189 function roadWidthByClass(casing){ 190 return ['match',['coalesce',['get','class'],'minor'], 191 'motorway',roadWidthExpr('motorway',casing), 192 'trunk',roadWidthExpr('motorway',casing), 193 'primary',roadWidthExpr('primary',casing), 194 'secondary',roadWidthExpr('secondary',casing), 195 'tertiary',roadWidthExpr('secondary',casing), 196 'service',roadWidthExpr('path',casing), 197 'track',roadWidthExpr('path',casing), 198 'path',roadWidthExpr('path',casing), 199 'pedestrian',roadWidthExpr('path',casing), 200 'steps',roadWidthExpr('path',casing), 201 roadWidthExpr('minor',casing) 202 ]; 203 } 204 205 function roadOpacityByClass(){ 206 const faint=['interpolate',['linear'],['zoom'],14.2,0,15.0,.12,16,.34,18,.62,20,.82]; 207 return ['match',['coalesce',['get','class'],'minor'], 208 'service',faint, 209 'track',faint, 210 'path',faint, 211 'pedestrian',faint, 212 'steps',faint, 213 1 214 ]; 215 } 216 217 function hasLegacyRoadStroke(cfg){ 218 return parseLegacyStyles(cfg).some(rule=>{ 219 const f=String(rule.featureType||'all').toLowerCase(); 220 const e=String(rule.elementType||'all').toLowerCase(); 221 if(!(f==='road'||f.startsWith('road.')))return false; 222 if(!(e==='geometry.stroke'||e.startsWith('geometry.stroke')))return false; 223 return (Array.isArray(rule.stylers)?rule.stylers:[]).some(st=>st.visibility!=='off'&&(st.color||st.visibility==='on')); 224 }); 225 } 226 227 function matchesLegacyFeature(layer,group,feature){ 228 feature=String(feature||'all').toLowerCase();if(feature==='all')return true; 229 if(feature==='water')return group==='water'; 230 if(feature==='road')return group==='road'||group==='roadLabels'; 231 if(feature==='road.highway'){ 232 // A single OpenMapTiles major-name layer mixes trunk + arterials, so do not 233 // hide that whole symbol layer. Its highway members are filtered later. 234 if(group==='roadLabels')return false;
235 const k=roadKind(layer);return k==='motorway'||k==='primary'; 236 } 237 if(feature==='road.arterial'){ 238 if(group==='roadLabels')return false; 239 const k=roadKind(layer);return k==='primary'||k==='secondary'; 240 } 241 if(feature==='road.local')return group==='road'&&['minor','path'].includes(roadKind(layer)); 242 if(feature.startsWith('road.'))return group==='road'||group==='roadLabels'; 243 if(feature.startsWith('poi'))return group==='poi'||group==='park';if(feature==='transit')return group==='transit'; 244 if(feature==='administrative')return group==='administrative';if(feature==='landscape')return group==='landscape';return false; 245 } 246 247 function matchesLegacyElement(layer,group,element){ 248 element=String(element||'all').toLowerCase(); 249 if(element==='all')return true; 250 if(element==='labels')return layer.type==='symbol'; 251 if(element==='labels.icon')return layer.type==='symbol'; 252 if(element==='labels.text'||element.startsWith('labels.text.'))return layer.type==='symbol'; 253 if(element==='geometry'||element.startsWith('geometry.'))return layer.type!=='symbol' && layer.type!=='background'; 254 return true; 255 } 256 257 function setFilterSafe(map,id,filter){try{map.setFilter(id,filter);}catch(e){}} 258 function setZoomRangeSafe(map,id,min,max){try{map.setLayerZoomRange(id,min,max);}catch(e){}} 259 260 // Resolve a color from the user's legacy Snazzy JSON before falling back to 261 // Elementor's color controls. This matters because the legacy JSON is the 262 // authoritative visual source in JSON mode; a later calibration pass must not 263 // accidentally reintroduce Elementor's default green park color. 264 function legacyFeatureColor(cfg,feature,element,fallback){ 265 let color=fallback; 266 parseLegacyStyles(cfg).forEach(rule=>{ 267 const f=String(rule.featureType||'all').toLowerCase(); 268 const e=String(rule.elementType||'all').toLowerCase(); 269 const featureMatch=f===String(feature).toLowerCase(); 270 const elementMatch=e==='all'||e===String(element).toLowerCase()|| 271 (String(element).toLowerCase()==='geometry'&&e.startsWith('geometry')); 272 if(!featureMatch||!elementMatch)return; 273 (Array.isArray(rule.stylers)?rule.stylers:[]).forEach(st=>{ 274 if(st.color&&/^#[0-9a-f]{6}$/i.test(st.color))color=st.color; 275 }); 276 }); 277 return color; 278 } 279 280 function roadTextVisibility(cfg,kind){ 281 const legacy=parseLegacyStyles(cfg); 282 let visible=true; 283 legacy.forEach(rule=>{ 284 const feature=String(rule.featureType||'all').toLowerCase(); 285 const element=String(rule.elementType||'all').toLowerCase(); 286 if(!(element==='all'||element==='labels'||element==='labels.text'||element.startsWith('labels.text.')))return; 287 const featureMatches=feature==='all'||feature==='road'|| 288 (feature==='road.highway'&&(kind==='motorway'||kind==='highway'))|| 289 (feature==='road.arterial'&&kind==='arterial')|| 290 (feature==='road.local'&&kind==='minor'); 291 if(!featureMatches)return; 292 (Array.isArray(rule.stylers)?rule.stylers:[]).forEach(st=>{ 293 if(st.visibility==='off')visible=false; 294 if(st.visibility==='on'||st.visibility==='simplified')visible=true; 295 }); 296 }); 297 return visible; 298 } 299 300 function calibrateGoogleCartography(map,cfg){ 301 const style=map.getStyle();if(!style||!style.layers)return; 302 const v=cfg.visibility||{},c=googleLikePalette(cfg); 303 const parkColor=legacyFeatureColor(cfg,'poi.park','geometry',c.park); 304 const landscapeColor=legacyFeatureColor(cfg,'landscape','all',c.geometry); 305 const waterColor=legacyFeatureColor(cfg,'water','all',c.water); 306 const roadFillColor=legacyFeatureColor(cfg,'road','geometry.fill','#5b5b5a'); 307 const roadStrokeColor=legacyFeatureColor(cfg,'road','geometry.stroke',c.roadStroke); 308 const useRoadStroke=hasLegacyRoadStroke(cfg); 309 const arterialLabels=!!v.labels&&!!v.roadLabels&&roadTextVisibility(cfg,'arterial'); 310 const minorLabels=!!v.labels&&!!v.roadLabels&&roadTextVisibility(cfg,'minor'); 311 312 style.layers.forEach(layer=>{ 313 const id=layer.id,lid=String(id||'').toLowerCase(),group=layerGroup(layer); 314 const sl=String(layer['source-layer']||'').toLowerCase(); 315 316 if(group==='road'&&layer.type==='line'){ 317 const casing=/casing|outline/.test(lid); 318 setPaintSafe(map,id,'line-width',roadWidthByClass(casing));
319 setLayoutSafe(map,id,'line-cap','round'); 320 setLayoutSafe(map,id,'line-join','round'); 321 322 if(casing){ 323 // The supplied Google/Snazzy style specifies geometry.fill, not a 324 // geometry.stroke. Google therefore reads as a single clean dark line, 325 // not a thick dark fill + equally dark casing stack. 326 setPaintSafe(map,id,'line-color',useRoadStroke?roadStrokeColor:landscapeColor); 327 setPaintSafe(map,id,'line-opacity',useRoadStroke ? .70 : 0); 328 }else{ 329 setPaintSafe(map,id,'line-color',roadFillColor); 330 setPaintSafe(map,id,'line-opacity',roadOpacityByClass()); 331 } 332 } 333 334 if(group==='roadLabels'&&layer.type==='symbol'){ 335 if(/shield|ref|route-number|route_number/.test(lid)){setLayoutSafe(map,id,'visibility','none');return;} 336 if(/path/.test(lid)){setLayoutSafe(map,id,'visibility','none');return;} 337 338 if(/major|primary|trunk|secondary|tertiary/.test(lid)){ 339 setLayoutSafe(map,id,'visibility',arterialLabels?'visible':'none'); 340 // Surface arterials only. Motorways remain unlabeled per the legacy style; 341 // trunk is retained because PR urban arterials are frequently classified 342 // as trunk in OSM even when Google labels them as ordinary avenues. 343 const arterialFilter=['match',['get','class'], 344 'motorway',false, 345 'trunk',true, 346 'primary',true, 347 'secondary',true, 348 'tertiary',true, 349 false 350 ]; 351 setFilterSafe(map,id,Array.isArray(layer.filter)?['all',layer.filter,arterialFilter]:arterialFilter); 352 setZoomRangeSafe(map,id,11.8,24); 353 setLayoutSafe(map,id,'text-size',['interpolate',['linear'],['zoom'],12,10.8,14,13.1,16,14.2,18,15.2]); 354 setLayoutSafe(map,id,'symbol-spacing',['interpolate',['linear'],['zoom'],12,430,14,285,16,245,18,210]); 355 setLayoutSafe(map,id,'text-padding',2.2); 356 }else if(/minor|street|local/.test(lid)){ 357 setLayoutSafe(map,id,'visibility',minorLabels?'visible':'none'); 358 const localFilter=['match',['get','class'], 359 'service',false,'track',false,'path',false,'pedestrian',false,'steps',false, 360 true 361 ]; 362 setFilterSafe(map,id,Array.isArray(layer.filter)?['all',layer.filter,localFilter]:localFilter); 363 setZoomRangeSafe(map,id,15.15,24); 364 setLayoutSafe(map,id,'text-size',['interpolate',['linear'],['zoom'],15.15,10.8,16.5,12.2,18,13.6,20,14.6]); 365 setLayoutSafe(map,id,'symbol-spacing',['interpolate',['linear'],['zoom'],15.15,300,17,235,19,195]); 366 setLayoutSafe(map,id,'text-padding',2.0); 367 }else{ 368 setLayoutSafe(map,id,'visibility','none'); 369 } 370 371 setLayoutSafe(map,id,'symbol-placement','line'); 372 setLayoutSafe(map,id,'text-rotation-alignment','map'); 373 setLayoutSafe(map,id,'text-keep-upright',true); 374 setLayoutSafe(map,id,'text-allow-overlap',false); 375 setLayoutSafe(map,id,'text-ignore-placement',false); 376 // The road/all lightness:+45 rule in the original Google style lightens 377 // road names considerably. Emulate that explicitly rather than leaving 378 // the much darker generic Elementor label color. 379 setPaintSafe(map,id,'text-color','#8a8a8a'); 380 setPaintSafe(map,id,'text-halo-color','#ffffff'); 381 setPaintSafe(map,id,'text-halo-width',1.85); 382 setPaintSafe(map,id,'text-halo-blur',0.16); 383 } 384 385 // Flat monochrome water. This also neutralizes colored waterway strokes. 386 if(group==='water'){ 387 if(layer.type==='fill')setPaintSafe(map,id,'fill-color',waterColor); 388 if(layer.type==='line'){ 389 setPaintSafe(map,id,'line-color',waterColor); 390 setPaintSafe(map,id,'line-opacity',1); 391 } 392 } 393 394 // GOOGLE-STYLE GROUND FIGURE HIERARCHY: 395 // shade the enclosing *site/landscape* polygon, not every child feature. 396 // This restores the solid gray park/recreation masses visible in the 397 // reference while keeping pitches, playgrounds, buildings and micro- 398 // landcover from fragmenting the figure. 399 if(layer.type==='fill'&&(sl==='landcover'||sl==='landuse')){ 400 // OpenMapTiles deliberately collapses many park-ish OSM tags into 401 // landcover.class='grass'. The original tag survives in `subclass`. 402 // Therefore class-first/coalesce logic loses exactly the large park / 403 // recreation polygons we want. Inspect class AND subclass separately. 404 const parentSubclasses=[ 405 'park','recreation_ground','recreation-ground','golf_course','golf-course', 406 'garden','botanical_garden','botanical-garden','village_green','nature_reserve', 407 'nature-reserve','forest','wood','cemetery' 408 ]; 409 const parentLanduseClasses=[ 410 'cemetery','theme_park','zoo' 411 ]; 412 const childDetailClasses=[ 413 'pitch','playground','track','parking','parking_space','parking-space', 414 'commercial','industrial','retail','residential','school','university', 415 'kindergarten','college','hospital','garages','bus_station','stadium' 416 ]; 417 const cls=['coalesce',['get','class'],''];
418 const sub=['coalesce',['get','subclass'],'']; 419 const isParent=[ 420 'any', 421 ['in',sub,['literal',parentSubclasses]], 422 ['in',cls,['literal',parentLanduseClasses]], 423 ['==',cls,'wood'] 424 ]; 425 setPaintSafe(map,id,'fill-color',['case',isParent,parkColor,landscapeColor]); 426 setPaintSafe(map,id,'fill-opacity',[ 427 'case', 428 isParent, v.parks ? 0.90 : 0, 429 ['in',cls,['literal',childDetailClasses]],0, 430 0 431 ]); 432 setPaintSafe(map,id,'fill-outline-color','rgba(0,0,0,0)'); 433 }else if(sl==='park'&&layer.type==='fill'){ 434 // OpenMapTiles' dedicated `park` source-layer already represents broad 435 // protected/nature-reserve parent polygons. Do not filter it by a 436 // small class whitelist: doing so made valid park polygons disappear. 437 setPaintSafe(map,id,'fill-color',parkColor); 438 setPaintSafe(map,id,'fill-opacity',v.parks ? 0.90 : 0); 439 setPaintSafe(map,id,'fill-outline-color','rgba(0,0,0,0)'); 440 }else if(group==='park'&&layer.type==='fill'){ 441 // Dedicated style layers outside the core landcover/landuse/park sources 442 // stay monochrome, but are kept restrained. 443 setPaintSafe(map,id,'fill-color',parkColor); 444 setPaintSafe(map,id,'fill-opacity',v.parks ? 0.90 : 0); 445 setPaintSafe(map,id,'fill-outline-color','rgba(0,0,0,0)'); 446 } 447 448 // No colored outlines around parks/landuse parcels, and no detailed 449 // lot/recreation linework. Google reference reads as clean figure-ground. 450 if(layer.type==='line'&&(sl==='landcover'||sl==='landuse'||group==='park')){ 451 setPaintSafe(map,id,'line-color',landscapeColor); 452 setPaintSafe(map,id,'line-opacity',0); 453 } 454 455 // Strict reference cleanup: the source screenshot is intentionally sparse. 456 // Suppress source-specific site/lot/airport/rail/ferry detail that is not 457 // part of the Google/Snazzy figure-ground language. 458 if(/aeroway|runway|taxiway|apron|ferry|railway|rail-|pier|housenumber|house-number/.test(lid) || 459 /aeroway|transportation_name_mid|housenumber/.test(sl)){ 460 setLayoutSafe(map,id,'visibility','none'); 461 } 462 463 // Any remaining non-core decorative fill/line should collapse into the 464 // base landscape instead of introducing a third/fourth gray or color. 465 if((layer.type==='fill'||layer.type==='line') && group==='other'){ 466 if(layer.type==='fill'){ 467 setPaintSafe(map,id,'fill-color',landscapeColor); 468 setPaintSafe(map,id,'fill-opacity',0); 469 }else{ 470 setPaintSafe(map,id,'line-opacity',0); 471 } 472 } 473 }); 474 } 475 476 function applyGoogleLikeStyle(map,cfg){ 477 const c=googleLikePalette(cfg),v=cfg.visibility||{},legacy=parseLegacyStyles(cfg),style=map.getStyle();if(!style||!style.layers)return; 478 style.layers.forEach(layer=>{ 479 const id=layer.id,group=layerGroup(layer),lid=String(id).toLowerCase(); 480 // The Google-style contact map is deliberately flat and sparse. Liberty's 481 // hillshade/raster/3D/decorative layers otherwise leak terrain colors through 482 // legacy Snazzy JSON, even when Google would treat them as landscape geometry. 483 if(layer.type==='hillshade'||layer.type==='raster'||layer.type==='fill-extrusion'||/hillshade|terrain|aerial|satellite/.test(lid)){setLayoutSafe(map,id,'visibility','none');return;} 484 if(layer.type==='fill'){setPaintSafe(map,id,'fill-pattern',null);} 485 // Polygonal transportation surfaces are visual noise in the reference and, 486 // if treated as roads, merge dense entrances/exits into solid dark blobs. 487 if(group==='transportationArea'){setLayoutSafe(map,id,'visibility','none');return;} 488 if(group==='building'){setLayoutSafe(map,id,'visibility','none');return;} 489 if(group==='labels'&&!v.labels){setLayoutSafe(map,id,'visibility','none');return;} 490 if(group==='roadLabels'&&(!v.labels||!v.roadLabels)){setLayoutSafe(map,id,'visibility','none');return;} 491 if(group==='poi'&&!v.poi){setLayoutSafe(map,id,'visibility','none');return;}
492 if(group==='transit'&&!v.transit){setLayoutSafe(map,id,'visibility','none');return;} 493 if(group==='park'&&!v.parks){setLayoutSafe(map,id,'visibility','none');return;} 494 if(group==='landscape'&&!v.landscape){setLayoutSafe(map,id,'visibility','none');return;} 495 if(group==='administrative'&&!v.administrative){setLayoutSafe(map,id,'visibility','none');return;} 496 497 if(layer.type==='background')setPaintSafe(map,id,'background-color',c.geometry); 498 if(layer.type==='fill'){ 499 if(group==='water')setPaintSafe(map,id,'fill-color',c.water); 500 else if(group==='park')setPaintSafe(map,id,'fill-color',c.park); 501 else if(group==='poi')setPaintSafe(map,id,'fill-color',c.poi); 502 else if(group==='landscape')setPaintSafe(map,id,'fill-color',c.geometry); 503 } 504 if(layer.type==='line'){ 505 if(group==='administrative'){setPaintSafe(map,id,'line-color',c.administrative);setPaintSafe(map,id,'line-width',['interpolate',['linear'],['zoom'],6,.35,12,.75,17,1.05]);} 506 if(group==='road'){ 507 const casing=/casing|outline/.test(lid),kind=roadKind(layer); 508 setPaintSafe(map,id,'line-color',casing?c.roadStroke:(kind==='motorway'||kind==='primary'?c.highway:c.road)); 509 setPaintSafe(map,id,'line-width',roadWidthExpr(kind,casing)); 510 setPaintSafe(map,id,'line-opacity',1); 511 } 512 } 513 if(layer.type==='symbol'){ 514 setPaintSafe(map,id,'text-color',c.label);setPaintSafe(map,id,'text-halo-color',c.labelHalo);setPaintSafe(map,id,'text-halo-width',1.15); 515 if(group==='roadLabels')setLayoutSafe(map,id,'text-size',['interpolate',['linear'],['zoom'],9,8.5,13,9.5,17,10.5]); 516 else setLayoutSafe(map,id,'text-size',['interpolate',['linear'],['zoom'],7,9,12,10,17,11.5]); 517 } 518 519 legacy.forEach(rule=>{ 520 if(!matchesLegacyFeature(layer,group,rule.featureType))return; 521 const el=String(rule.elementType||'all').toLowerCase(); 522 if(!matchesLegacyElement(layer,group,el))return; 523 const stylers=Array.isArray(rule.stylers)?rule.stylers:[]; 524 stylers.forEach(st=>{ 525 // Google 'simplified' means keep the feature but reduce detail. On a 526 // vector style we preserve visibility and let our widths/label rules do that. 527 if(st.visibility==='off')setLayoutSafe(map,id,'visibility','none'); 528 if(st.visibility==='on'||st.visibility==='simplified')setLayoutSafe(map,id,'visibility','visible'); 529 if(st.color&&/^#[0-9a-f]{6}$/i.test(st.color)){ 530 if(layer.type==='background'&&el!=='labels')setPaintSafe(map,id,'background-color',st.color); 531 if(layer.type==='fill')setPaintSafe(map,id,'fill-color',st.color); 532 if(layer.type==='line')setPaintSafe(map,id,'line-color',st.color); 533 if(layer.type==='symbol'&&(el.includes('text.fill')||el==='all'||el==='labels'))setPaintSafe(map,id,'text-color',st.color); 534 if(layer.type==='symbol'&&el.includes('text.stroke'))setPaintSafe(map,id,'text-halo-color',st.color); 535 } 536 }); 537 }); 538 }); 539 540 // Legacy Google/Snazzy rules establish color/visibility first. This final pass 541 // calibrates hierarchy and label density without changing the user's palette. 542 calibrateGoogleCartography(map,cfg); 543 } 544 545 function markerElement(item){ 546 const size=Math.max(8,Math.min(160,Number(item.size)||32)),el=document.createElement('div');el.className='aud-sgm-marker';el.style.setProperty('--aud-pin-size',(item.image?size:Math.round(size*1.12))+'px'); 547 if(item.image){const img=document.createElement('img');img.src=item.image;img.alt=item.title||'';el.appendChild(img);return el;} 548 const color=hex(item.color,'#3367d6'); 549 el.innerHTML='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2C7.58 2 4 5.58 4 10c0 5.25 8 12 8 12s8-6.75 8-12c0-4.42-3.58-8-8-8z" fill="'+color+'"/><circle cx="12" cy="10" r="3" fill="#fff"/></svg>'; 550 return el; 551 } 552 553 function addStreetViewControl(map){ 554 class StreetViewControl{ 555 onAdd(m){this.map=m;this.container=document.createElement('div');this.container.className='maplibregl-ctrl maplibregl-ctrl-group';const b=document.createElement('button');b.className='aud-sgm-streetview-ctrl';b.type='button';b.title='Open Street View';b.setAttribute('aria-label','Open Street View');b.textContent='SV';b.addEventListener('click',()=>{const c=m.getCenter(),u='https://www.google.com/maps/@?api=1&map_action=pano&viewpoint='+encodeURIComponent(c.lat+','+c.lng);window.open(u,'_blank','noopener');});this.container.appendChild(b);return this.container;} onRemove(){this.container?.remove();this.map=null;} 556 } 557 map.addControl(new StreetViewControl(),'top-right'); 558 } 559 560 function applyMobilePresentation(el,cfg){ 561 const shell=el.closest('.aud-sgm-shell');
561if(!shell)return;const mp=cfg.mobilePresentation||{},mobile=window.innerWidth<=767; 562 if(mobile&&mp.sizeMode==='aspect_ratio'){shell.classList.add('aud-sgm-mobile-thumbnail');shell.style.width='100%';shell.style.maxWidth='100%';shell.style.aspectRatio=String(mp.aspectRatio||'4/5');shell.style.marginLeft='0';shell.style.marginRight='0';el.style.height='100%';} 563 else{shell.classList.remove('aud-sgm-mobile-thumbnail');shell.style.removeProperty('aspect-ratio');} 564 el.classList.toggle('aud-sgm-hide-mobile-controls',!!(mobile&&mp.hideControls)); 565 } 566 567 function applyDesktopViewportHeight(el,cfg,map){ 568 if(!el)return; 569 const desktop=window.innerWidth>767; 570 if(!desktop||!cfg.desktopFillViewport){ 571 if(el.dataset.audSgmViewportHeight==='1'){ 572 el.style.removeProperty('height'); 573 el.style.removeProperty('min-height'); 574 delete el.dataset.audSgmViewportHeight; 575 try{map&&map.resize();}catch(e){} 576 } 577 return; 578 } 579 const top=el.getBoundingClientRect().top; 580 const h=Math.max(240,Math.round(window.innerHeight-top)); 581 const px=h+'px'; 582 if(el.style.height!==px||el.dataset.audSgmViewportHeight!=='1'){ 583 el.style.height=px; 584 el.style.minHeight=px; 585 el.dataset.audSgmViewportHeight='1'; 586 try{map&&map.resize();}catch(e){} 587 } 588 } 589 590 function bindDesktopViewportHeight(el,cfg,map){ 591 if(!cfg.desktopFillViewport)return; 592 let raf=0; 593 const update=()=>{cancelAnimationFrame(raf);raf=requestAnimationFrame(()=>applyDesktopViewportHeight(el,cfg,map));}; 594 update(); 595 window.addEventListener('resize',update,{passive:true}); 596 window.addEventListener('orientationchange',update,{passive:true}); 597 window.addEventListener('load',update,{once:true}); 598 if(window.ResizeObserver){ 599 const ro=new ResizeObserver(update); 600 const wrapper=el.closest('.elementor-element')||el.parentElement; 601 if(wrapper)ro.observe(wrapper); 602 el.__audSgmViewportObserver=ro; 603 } 604 el.__audSgmViewportUpdate=update; 605 } 606 607 function applyInitialFocalPoint(map,cfg){ 608 const p=activeFocalPoint(cfg),w=map.getContainer()?.clientWidth||0,h=map.getContainer()?.clientHeight||0;if(!w||!h)return; 609 const dx=w*(.5-p.x/100),dy=h*(.5-p.y/100);if(Math.abs(dx)>.2||Math.abs(dy)>.2)map.panBy([dx,dy],{duration:0}); 610 } 611 612 function configureInteractions(map,cfg){ 613 const c=cfg.controls||{}; 614 const set=(handler,on)=>{try{on?handler.enable():handler.disable();}catch(e){}}; 615 set(map.scrollZoom,!!c.scrollwheel);set(map.dragPan,!!c.draggable);set(map.doubleClickZoom,!c.disableDoubleClickZoom);set(map.keyboard,!!c.keyboardShortcuts); 616 if(c.gestureHandling==='none'){set(map.scrollZoom,false);set(map.dragPan,false);set(map.touchZoomRotate,false);set(map.doubleClickZoom,false);} 617 } 618 619 async function initMap(el){ 620 if(!el||el.dataset.audSgmReady==='1')return;el.dataset.audSgmReady='1';let cfg={};try{cfg=JSON.parse(el.getAttribute('data-aud-sgm')||'{}');}catch(e){} 621 showStatus(el,'Loading mapâ¦'); 622 try{ 623 if(!window.maplibregl)throw new Error(NS.messages?.loadError||'MapLibre could not load.');registerPMTiles(); 624 let center,geocodeWarning=''; 625 if(validLatLng(cfg.lat,cfg.lng))center=[Number(cfg.lng),Number(cfg.lat)]; 626 else if(cfg.address){try{center=await geocode(cfg.address);}catch(e){center=[Number(cfg.fallbackLng)||-66.5901,Number(cfg.fallbackLat)||18.2208];geocodeWarning=NS.messages?.geocodeError||'Address lookup unavailable; using fallback center.';console.warn('[A+UD Map]',e);}} 627 else center=[Number(cfg.fallbackLng)||-66.5901,Number(cfg.fallbackLat)||18.2208]; 628 629 let style;if(NS.sourceMode==='pmtiles'){ 630 if(!String(NS.pmtilesUrl||'').trim())throw new Error(NS.messages?.pmtilesMissing||'PMTiles URL missing.');style=builtInPMTilesStyle(cfg); 631 } else style=String(NS.styleUrl||'https://tiles.openfreemap.org/styles/liberty'); 632 633 applyMobilePresentation(el,cfg); 634 applyDesktopViewportHeight(el,cfg,null); 635 const mobile=window.innerWidth<=767, controls=cfg.controls||{}; 636 const map=new window.maplibregl.Map({container:el,style,center,zoom:activeZoom(cfg),bearing:Number(cfg.heading)||0,pitch:Number(cfg.tilt)||0,minZoom:hasNumber(cfg.minZoom)?Number(cfg.minZoom):0,maxZoom:hasNumber(cfg.maxZoom)?Number(cfg.maxZoom):22,attributionControl:false,cooperativeGestures:!!(controls.scrollwheel&&controls.gestureHandling==='cooperative'),dragRotate:!!controls.rotateControl,pitchWithRotate:!!controls.rotateControl,fadeDuration:0});
637 bindDesktopViewportHeight(el,cfg,map); 638 el.__audSgmMap=map;el.__audSgmPrimaryPosition=center; 639 if(window.maplibregl.AttributionControl){ 640 map.addControl(new window.maplibregl.AttributionControl({compact:mobile}),'bottom-right'); 641 } 642 configureInteractions(map,cfg); 643 if(cfg.restriction?.latLngBounds){const b=cfg.restriction.latLngBounds;map.setMaxBounds([[Number(b.west),Number(b.south)],[Number(b.east),Number(b.north)]]);} 644 if(!mobile||!cfg.mobilePresentation?.hideControls){ 645 if(controls.zoomControl||controls.rotateControl)map.addControl(new window.maplibregl.NavigationControl({showZoom:!!controls.zoomControl,showCompass:!!controls.rotateControl,visualizePitch:!!controls.rotateControl}),'top-right'); 646 if(controls.fullscreenControl&&window.maplibregl.FullscreenControl)map.addControl(new window.maplibregl.FullscreenControl(),'top-right'); 647 if(controls.scaleControl&&window.maplibregl.ScaleControl)map.addControl(new window.maplibregl.ScaleControl({unit:'imperial'}),'bottom-left'); 648 if(controls.streetViewControl)addStreetViewControl(map); 649 } 650 651 const bounds=new window.maplibregl.LngLatBounds();let markerCount=0; 652 function addMarker(pos,item,isPrimary){ 653 const node=markerElement(item),marker=new window.maplibregl.Marker({element:node,anchor:'bottom'}).setLngLat(pos).addTo(map);bounds.extend(pos);markerCount++; 654 const openPopup=(content)=>new window.maplibregl.Popup({offset:18,className:'aud-sgm-popup',closeButton:true}).setLngLat(pos).setDOMContent(content).addTo(map); 655 if(isPrimary){ 656 const action=item.action||'popup',touch=isTouchLike(); 657 if(touch&&(action==='directions'||action==='google_maps'))node.addEventListener('click',e=>{e.preventDefault();e.stopPropagation();const u=mapActionUrl(action,pos);if(u)window.open(u,'_blank','noopener');}); 658 else if(touch&&item.touchTrigger==='card'&&action!=='none')node.addEventListener('click',e=>{e.stopPropagation();openPopup(primaryActionCard(item,pos));}); 659 else if(action==='popup'){if(item.title||item.description||item.link)node.addEventListener('click',e=>{e.stopPropagation();openPopup(popupHtml(item));});} 660 else if(action!=='none'){ 661 const evt=touch?'click':(item.desktopTrigger==='click'?'click':'dblclick');node.addEventListener(evt,e=>{e.preventDefault();e.stopPropagation();const u=mapActionUrl(action,pos);if(u)window.open(u,'_blank','noopener');}); 662 } 663 } else if(item.title||item.description||item.link)node.addEventListener('click',e=>{e.stopPropagation();openPopup(popupHtml(item));}); 664 return marker; 665 } 666 667 map.once('load',async()=>{ 668 applyGoogleLikeStyle(map,cfg); 669 if(cfg.primaryMarker?.show)addMarker(center,cfg.primaryMarker,true); 670 for(const poi of (cfg.pois||[])){ 671 let pos=null;if(validLatLng(poi.lat,poi.lng))pos=[Number(poi.lng),Number(poi.lat)];else if(poi.address){try{pos=await geocode(poi.address);}catch(e){}} 672 if(pos)addMarker(pos,poi,false); 673 } 674 if(cfg.fitToMarkers&&markerCount>1)map.fitBounds(bounds,{padding:48,duration:0}); 675 requestAnimationFrame(()=>applyInitialFocalPoint(map,cfg)); 676 showStatus(el,geocodeWarning); 677 applyAUDContactCompositionV203(); 678 }); 679 let mapLoaded=false; 680 map.on('load',()=>{mapLoaded=true;}); 681 map.on('error',e=>{ 682 if(e?.error)console.warn('[A+UD MapLibre]',e.error); 683 if(!mapLoaded){ 684 const msg=e?.error?.message||'The vector basemap source could not load.'; 685 showStatus(el,'Map source error: '+msg); 686 } 687 }); 688 }catch(err){el.dataset.audSgmReady='0';showStatus(el,err?.message||NS.messages?.loadError||'Map could not load.');console.warn('[A+UD Map]',err);} 689 } 690 691 function setImportant(el,prop,value){if(el)el.style.setProperty(prop,value,'important');} 692 function clearImportant(el,props){if(el)props.forEach(prop=>el.style.removeProperty(prop));} 693 function enforceMobileHeader(header,mobile){ 694 if(!header)return;const props=['position','inset','top','right','bottom','left','width','z-index','margin','background','background-color','background-image','box-shadow']; 695 if(!mobile){clearImportant(header,props);header.classList.remove('aud-sgm-v205-mobile-header');return;} 696 header.classList.add('aud-sgm-v205-mobile-header');[['position','relative'],['inset','auto'],['top','auto'],['right','auto'],['bottom','auto'],['left','auto'],['width','100%'],['z-index','20'],['margin','0'],['background','#FFFFFF'],['background-color','#F
696FFFFF'],['background-image','none'],['box-shadow','none']].forEach(x=>setImportant(header,x[0],x[1])); 697 } 698 function suppressMobileMapUI(mapEl){if(!mapEl||window.innerWidth>767)return;mapEl.classList.add('aud-sgm-hide-mobile-controls');} 699 function recenterMobileContactMap(mapEl,cfg){ 700 if(!mapEl||window.innerWidth>767)return;const map=mapEl.__audSgmMap,pos=mapEl.__audSgmPrimaryPosition;if(!map||!pos)return; 701 const doCenter=()=>{map.resize();map.setCenter(pos);const z=activeZoom(cfg||{});if(Number.isFinite(z))map.setZoom(z);};requestAnimationFrame(()=>requestAnimationFrame(doCenter));setTimeout(doCenter,120);setTimeout(doCenter,360); 702 } 703 704 function applyAUDContactCompositionV203(){ 705 const mobile=window.matchMedia('(max-width: 767px)').matches; 706 const header=document.getElementById('aud-header')||document.querySelector('.elementor-element-6d505f2'); 707 const card=document.getElementById('aud-contact-card')||document.querySelector('.elementor-element-e35d4f8'); 708 const mapWrap=document.getElementById('aud-contact-map')||document.querySelector('.elementor-element-13eef00');if(!card||!mapWrap)return; 709 let stage=document.getElementById('aud-contact-stage');if(!stage||card.parentElement!==stage||mapWrap.parentElement!==stage){if(card.parentElement&&card.parentElement===mapWrap.parentElement)stage=card.parentElement;else stage=document.querySelector('.elementor-element-00049d0')||card.parentElement;}if(!stage)return; 710 const mapEl=mapWrap.querySelector('.aud-sgm-map[data-aud-sgm]');let cfg={};if(mapEl){try{cfg=JSON.parse(mapEl.getAttribute('data-aud-sgm')||'{}');}catch(e){}} 711 const mp=cfg.mobilePresentation||{},enabled=mp.contactComposition!==false,all=[header,stage,card,mapWrap].filter(Boolean); 712 if(!mobile||!enabled){document.body.classList.remove('aud-sgm-contact-v203','aud-sgm-contact-v205');all.forEach(el=>el.classList.remove('aud-sgm-v203-header','aud-sgm-v203-stage','aud-sgm-v203-card','aud-sgm-v203-map-wrap'));enforceMobileHeader(header,false);stage.style.removeProperty('--aud-sgm-mobile-map-w');stage.style.removeProperty('--aud-sgm-mobile-map-top');if(mapEl?.__audSgmMap)setTimeout(()=>mapEl.__audSgmMap.resize(),0);return;} 713 document.body.classList.add('aud-sgm-contact-v203','aud-sgm-contact-v205');if(header)header.classList.add('aud-sgm-v203-header');enforceMobileHeader(header,true);stage.classList.add('aud-sgm-v203-stage');card.classList.add('aud-sgm-v203-card');mapWrap.classList.add('aud-sgm-v203-map-wrap'); 714 const width=Math.max(110,Math.min(210,Number(mp.widthSize)||150));stage.style.setProperty('--aud-sgm-mobile-map-w',width+'px');const heading=card.querySelector('.elementor-widget-heading')||card.querySelector('h1,h2,h3,h4,h5,h6'); 715 const align=()=>{let top=0;if(heading){const sr=stage.getBoundingClientRect(),hr=heading.getBoundingClientRect();top=Math.max(0,hr.top-sr.top);}stage.style.setProperty('--aud-sgm-mobile-map-top',Math.round(top)+'px');if(mapEl&&mapEl.__audSgmMap)recenterMobileContactMap(mapEl,cfg);}; 716 requestAnimationFrame(()=>requestAnimationFrame(align));if(document.fonts&&document.fonts.ready)document.fonts.ready.then(align).catch(()=>{});if(mapEl){suppressMobileMapUI(mapEl);recenterMobileContactMap(mapEl,cfg);} 717 } 718 719 function scan(root){(root||document).querySelectorAll('.aud-sgm-map[data-aud-sgm]').forEach(initMap);} 720 document.addEventListener('DOMContentLoaded',()=>{applyAUDContactCompositionV203();scan(document);}); 721 window.addEventListener('load',()=>{applyAUDContactCompositionV203();scan(document);}); 722 window.addEventListener('resize',()=>{clearTimeout(window.__audSgmContactResize);window.__audSgmContactResize=setTimeout(applyAUDContactCompositionV203,80);}); 723 if(window.elementorFrontend&&window.elementorFrontend.hooks)window.elementorFrontend.hooks.addAction('frontend/element_ready/aud_snazzy_google_maps.default',function($scope){scan($scope&&$scope[0]?$scope[0]:document);}); 724 else window.addEventListener('elementor/frontend/init',()=>window.elementorFrontend.hooks.addAction('frontend/element_ready/aud_snazzy_google_maps.default',function($scope){scan($scope&&$scope[0]?$scope[0]:document);})); 725})(window,document);
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.