1/** 2 * @file 3 * Dynamically loads Leaflet CDN scripts when Klaro consent is given. 4 * 5 * External Leaflet scripts are stripped from the library definitions by 6 * drk_core_library_info_alter() and their URLs are passed via 7 * drupalSettings.drkCore.leafletCdnScripts. This file loads them in 8 * sequence when the Klaro "leaflet" service receives consent, ensuring 9 * correct dependency order (core before plugins) and no race conditions. 10 */ 11 12(function (Drupal, drupalSettings, once) { 13 14 'use strict'; 15 16 /** 17 * Load a list of script URLs sequentially, calling callback when all done. 18 * 19 * @param {string[]} urls 20 * @param {function} callback 21 */ 22 function loadScriptsSequentially(urls, callback) { 23 var index = 0; 24 25 function loadNext() { 26 if (index >= urls.length) { 27 callback(); 28 return; 29 } 30 var script = document.createElement('script'); 31 script.src = urls[index]; 32 script.onload = function () { 33 index++; 34 loadNext(); 35 }; 36 script.onerror = function () { 37 index++; 38 loadNext(); 39 }; 40 document.head.appendChild(script); 41 } 42 43 loadNext(); 44 } 45 46 /** 47 * Load CSS stylesheets by adding link elements to the head. 48 * 49 * @param {string[]} urls 50 */ 51 function loadStylesheets(urls) { 52 urls.forEach(function (url) { 53 var link = document.createElement('link'); 54 link.rel = 'stylesheet'; 55 link.href = url; 56 document.head.appendChild(link); 57 }); 58 } 59 60 /** 61 * Remove dead Leaflet map instances and re-initialize them. 62 */ 63 function reinitializeMaps() { 64 if (!Drupal.geolocation || !Drupal.geolocation.maps) { 65 return; 66 } 67 68 // Remove all leaflet-type map instances so they can be rebuilt with 69 // all CDN plugins available. This includes maps whose promise already 70 // resolved (leafletMap is set) AND maps whose promise rejected because 71 // the CDN scripts were not yet loaded (leafletMap is undefined). 72 var mapWrappers = []; 73 for (var i = Drupal.geolocation.maps.length - 1; i >= 0; i--) { 74 var map = Drupal.geolocation.maps[i]; 75 if (map.type === 'leaflet') { 76 if (map.leafletMap) { 77 try { 78 map.leafletMap.remove(); 79 } 80 catch (e) { 81 // Ignore errors during teardown (e.g. bouncemarker plugin 82 // trying to update marker positions on a partially destroyed map). 83 } 84 } 85 if (map.wrapper) { 86 // Clean up any partial Leaflet initialization on the container 87 // (e.g. _leaflet_id set by a failed L.map() call). 88 var container = map.wrapper.find('.geolocation-map-container').get(0); 89 if (container && container._leaflet_id) { 90 delete container._leaflet_id; 91 container.innerHTML = ''; 92 } 93 mapWrappers.push(map.wrapper.get(0)); 94 once.remove('geolocation-map-processed', map.wrapper.get(0)); 95 } 96 Drupal.geolocation.maps.splice(i, 1); 97 } 98 } 99 100 // Clear orphan once markers that don't belong to any live map. 101 var liveIds = []; 102 Drupal.geolocation.maps.forEach(function (map) { 103 if (map.type === 'leaflet') { 104 liveIds.push(map.id); 105 } 106 }); 107 once.find('geolocation-map-processed').forEach(function (el) { 108 if (liveIds.indexOf(el.id) === -1) { 109 once.remove('geolocation-map-processed', el); 110 } 111 }); 112 113 // Show Klaro wrappers BEFORE creating maps so containers have proper 114 // dimensions. Leaflet (and leaflet-rotate) fails on zero-size containers. 115 mapWrappers.forEach(function (wrapper) { 116 var containers = wrapper.querySelectorAll 117 ? wrapper.querySelectorAll('.geolocation-map-container') 118 : []; 119 for (var j = 0; j < containers.length; j++) { 120 var klaroWrapper = containers[j].closest('[data-name="leaflet"]'); 121 if (klaroWrapper && klaroWrapper.style.display === 'none') { 122 var origDisplay = klaroWrapper.getAttribute('data-original-display') || 'block'; 123 klaroWrapper.style.display = origDisplay; 124 125 // Protect against BigPipe re-hides: klaro.setup() runs on every 126 // attachBehaviors call and re-applies consent state. For 'accept 127 // once' consent is already false, so the wrapper gets hidden again.
128 // The observer re-shows it â unless consent was explicitly revoked. 129 if (!klaroWrapper._drkObserver) { 130 var observer = new MutationObserver(function () { 131 if (klaroWrapper.style.display === 'none' && !Drupal.drkLeafletLoader.revoked) { 132 klaroWrapper.style.display = origDisplay; 133 } 134 }); 135 observer.observe(klaroWrapper, {attributes: true, attributeFilter: ['style']}); 136 klaroWrapper._drkObserver = observer; 137 } 138 } 139 } 140 }); 141 142 // Re-attach behaviors scoped to map wrappers only. Using document 143 // as context would re-trigger all behaviors, including ones without 144 // once() guards (e.g. searchHeader), causing duplicate event handlers. 145 mapWrappers.forEach(function (wrapper) { 146 Drupal.attachBehaviors(wrapper, drupalSettings); 147 }); 148 149 // After map creation, call invalidateSize to ensure correct tile loading. 150 setTimeout(function () { 151 if (!Drupal.geolocation || !Drupal.geolocation.maps) { 152 return; 153 } 154 Drupal.geolocation.maps.forEach(function (map) { 155 if (map.leafletMap) { 156 map.leafletMap.invalidateSize(); 157 } 158 }); 159 }, 300); 160 } 161 162 Drupal.drkLeafletLoader = { 163 loaded: false, 164 initializing: false, 165 revoked: false, 166 167 /** 168 * Load all Leaflet CDN scripts/styles and reinitialize maps. 169 */ 170 load: function () { 171 if (this.initializing) { 172 return; 173 } 174 175 this.revoked = false; 176 177 if (this.loaded) { 178 this.initializing = true; 179 reinitializeMaps(); 180 this.initializing = false; 181 return; 182 } 183 184 var drkCore = drupalSettings.drkCore || {}; 185 var jsUrls = drkCore.leafletCdnScripts || []; 186 var cssUrls = drkCore.leafletCdnStyles || []; 187 188 if (!jsUrls.length) { 189 return; 190 } 191 192 loadStylesheets(cssUrls); 193 194 var self = this; 195 loadScriptsSequentially(jsUrls, function () { 196 self.loaded = true; 197 self.initializing = true; 198 reinitializeMaps(); 199 self.initializing = false; 200 }); 201 }, 202 203 /** 204 * Called by Klaro callback_code when consent is revoked. 205 */ 206 revoke: function () { 207 this.revoked = true; 208 } 209 }; 210 211 // When Klaro is not active for the current user, load CDN immediately. 212 Drupal.behaviors.drkLeafletAutoLoad = { 213 attach: function (context, settings) { 214 if (!once('drk-leaflet-auto-load', 'body', context).length) { 215 return; 216 } 217 if (!settings.klaro) { 218 Drupal.drkLeafletLoader.load(); 219 } 220 } 221 }; 222 223})(Drupal, drupalSettings, once);
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.