PageSourceSearch

https://www.menen.be/modules/custom/drk_core/js/klaro-leaflet-loader.js?v=1.x

js menen.be collected 2026-10-01 12:10:30 UTC 6,961 bytes, 223 lines download raw bytes

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.