PageSourceSearch

https://www.regiedesbatiments.be/modules/contrib/leaflet/js/leaflet.drupal.js?tluj98

js regiedesbatiments.be collected 2026-09-24 21:24:04 UTC 40,593 bytes, 1,120 lines download raw bytes

1(function($, Drupal, drupalSettings, once) {
2
3  "use strict";
4
5  Drupal.behaviors.leaflet = {
6    attach: function(context, settings) {
7
8      // For each Leaflet Map/id defined process with Leaflet Map and Features
9      // generation.
10      $.each(settings.leaflet, function(mapid, leaflet_settings) {
11
12        // Ensure the Leaflet Behavior is attached only once to each Leaflet map
13        // id element.
14        // @see https://www.drupal.org/project/leaflet/issues/3314762#comment-15044223
15        const leaflet_elements = $(once('behaviour-leaflet', '#' + mapid,));
16        leaflet_elements.each(function() {
17          const map_container = $(this);
18
19          // Function to load the Leaflet Map, based on the provided mapid.
20          function loadMap(mapid) {
21            // Process a new Leaflet Map only if the map container is empty.
22            // Avoid reprocessing a Leaflet Map already initialised.
23            if (map_container.data('leaflet') === undefined) {
24              map_container.data('leaflet', new Drupal.Leaflet(L.DomUtil.get(mapid), mapid, leaflet_settings.map));
25              if (leaflet_settings.features.length > 0) {
26                // Add Leaflet Map Features.
27                map_container.data('leaflet').add_features(leaflet_settings.features, true);
28              }
29
30              // Add the Leaflet map to data settings object to make it
31              // accessible and extendable from other Drupal.behaviors attached
32              // functions.
33              // @NOTE: i.e. this is used by the leaflet.widget.js.
34              leaflet_settings.lMap = map_container.data('leaflet').lMap;
35
36              // Add the Leaflet Map Markers to data settings object to make it
37              // also accessible and extendable.
38              leaflet_settings.markers = map_container.data('leaflet').markers;
39
40              // Set initial Map position to wrap its defined bounds.
41              map_container.data('leaflet').fitBounds();
42
43              // Set the start center and the start zoom.
44              if (!map_container.data('leaflet').start_center && !map_container.data('leaflet').start_zoom) {
45                map_container.data('leaflet').start_center = map_container.data('leaflet').lMap.getCenter();
46                map_container.data('leaflet').start_zoom = map_container.data('leaflet').lMap.getZoom();
47              }
48
49              // Define the global Drupal.Leaflet[mapid] object to be accessible
50              // from outside - NOTE: This should always be created after setting
51              // the (above) start center.
52              Drupal.Leaflet[mapid] = map_container.data('leaflet');
53
54              // Add the Map Geocoder Control if requested.
55              if (!Drupal.Leaflet[mapid].geocoder_control && Drupal.Leaflet.prototype.map_geocoder_control) {
56                const mapGeocoderControlDiv = document.createElement('div');
57                Drupal.Leaflet[mapid].geocoder_control = Drupal.Leaflet.prototype.map_geocoder_control(mapGeocoderControlDiv, mapid);
58                Drupal.Leaflet[mapid].geocoder_control.addTo(Drupal.Leaflet[mapid].lMap);
59                const geocoder_settings = drupalSettings.leaflet[mapid].map.settings.geocoder.settings;
60                Drupal.Leaflet.prototype.map_geocoder_control.autocomplete(mapid, geocoder_settings);
61              }
62
63              // Add the Layers Control, if initialised/existing.
64              if (Drupal.Leaflet[mapid].layer_control) {
65                Drupal.Leaflet[mapid].lMap.addControl(Drupal.Leaflet[mapid].layer_control);
66              }
67
68              // Add and Initialise the Map Reset View Control if requested.
69              if (!Drupal.Leaflet[mapid].reset_view_control &&
70                  map_container.data('leaflet').map_settings.reset_map &&
71                  map_container.data('leaflet').map_settings.reset_map.control) {
72                const map_reset_view_options = map_container.data('leaflet').map_settings.reset_map.options ?
73                  JSON.parse(map_container.data('leaflet').map_settings.reset_map.options) : {};
74                map_reset_view_options.latlng = map_container.data('leaflet').start_center;
75                map_reset_view_options.zoom = map_container.data('leaflet').start_zoom;
76                Drupal.Leaflet[mapid].reset_view_control = L.control.resetView(map_reset_view_options)
77                  .addTo(map_container.data('leaflet').lMap);
78              }
79
80              // Add and Initialise the Map Scale Control if requested.
81              if (!Drupal.Leaflet[mapid].map_scale_control &&
82                  map_container.data('leaflet').map_settings.map_scale &&
83                  map_container.data('leaflet').map_settings.map_scale.control) {
84                const map_scale_options = map_container.data('leaflet').map_settings.map_scale.options ?
85                  JSON.parse(map_container.data('leaflet').map_settings.map_scale.options) : {};
86                Drupal.Leaflet[mapid].map_scale_control = L.control.scale(map_scale_options)
87                  .addTo(map_container.data('leaflet').lMap);
88              }
89
90              // Add the Locate Control if requested.
91              if (!Drupal.Leaflet[mapid].locate_control &&
92                  map_container.data('leaflet').map_settings.locate &&
93                  map_container.data('leaflet').map_settings.locate.control) {
94                const locate_options = map_container.data('leaflet').map_settings.locate.options ?
95                  JSON.parse(map_container.data('leaflet').map_settings.locate.options) : {};
96                Drupal.Leaflet[mapid].locate_control = L.control.locate(locate_options)
97                  .addTo(map_container.data('leaflet').lMap);
98
99                // In case this Leaflet Map is not in a Widget Context, eventually perform the Automatic User Locate, if requested.
100                if (!leaflet_settings.hasOwnProperty('leaflet_widget') &&
101                    map_container.data('leaflet').map_settings.hasOwnProperty('locate') &&
102                    map_container.data('leaflet').map_settings.locate.automatic) {
103                  Drupal.Leaflet[mapid].locate_control.start();
104                }
105              }
106
107              // Add Fullscreen Control, if requested.
108              if (!Drupal.Leaflet[mapid].fullscreen_control &&
109                  map_container.data('leaflet').map_settings.fullscreen &&
110                  map_container.data('leaflet').map_settings.fullscreen.control) {
111                const map_fullscreen_options = map_container.data('leaflet').map_settings.fullscreen.options ?
112                  JSON.parse(map_container.data('leaflet').map_settings.fullscreen.options) : {};
113                Drupal.Leaflet[mapid].fullscreen_control = L.control.fullscreen(map_fullscreen_options)
114                  .addTo(map_container.data('leaflet').lMap);
115              }
116
117              // Attach Leaflet Map listeners On Popup Open.
118              leaflet_settings.lMap.on('popupopen', function(e) {
119                // On leaflet-ajax-popup selector, fetch and set Ajax content.
120                const element = e.popup._contentNode;
121                const content = $('*[data-leaflet-ajax-popup]', element);
122                if (content.length) {
123                  const url = content.data('leaflet-ajax-popup');
124                  Drupal.ajax({url: url}).execute()
125                    .done(function(data) {
126                      // Copy the html we received via AJAX to the popup, so we won't
127                      // have to make another AJAX call (#see 3258780).
128                      e.popup.setContent(data[0].data);
129
130                      // Attach drupal behaviors on new content.
131                      Drupal.attachBehaviors(element, drupalSettings);
132                    })
133                    .fail(function() {
134                      // In case of failing fetching data.
135                      e.popup.close();
136                    });
137                }
138
139                // Make the (eventually present) Tooltip disappear on Popup Open
140                // in case the Popup is generated from a _source.
141                if (e.popup._source) {
142                  const tooltip = e.popup._source.getTooltip();
143                  // not all features will have tooltips!
144                  if (tooltip) {
145                    // use opacity to make the tooltip disappear.
146                    tooltip.setOpacity(0);
147                  }
148                }
149              });
150
151              // Attach Leaflet Map listeners On Popup Close.
152              leaflet_settings.lMap.on('popupclose', function(e) {
153                // Make the (eventually present) Tooltip re-appear on Popup Close.
154                // in case the Popup is generated from a _source.
155                if (e.popup._source) {
156                  const tooltip = e.popup._source.getTooltip();
157                  if (tooltip) {
158                    tooltip.setOpacity(0.9);
159                  }
160                }
161              });
162
163              // Define and Trigger 'leafletMapInit' event to let other modules
164              // and custom js libraries to bind & rect on Leaflet Map
165              // generation and extend & interact with its definition,
166              // properties and contents / features.
167              // NOTE: don't change this trigger arguments,
168              // to preserve backwards compatibility.
169              $(document).trigger('leafletMapInit', [leaflet_settings.map, leaflet_settings.lMap, mapid, leaflet_settings.markers]);
170              // NOTE: Keep also this pre-existing event for backwards
171              // compatibility with Leaflet < 2.1.0.
172              $(document).trigger('leaflet.map', [leaflet_settings.map, leaflet_settings.lMap, mapid, leaflet_settings.markers]);
173            }
174          }
175
176          // If the IntersectionObserver API is available, create an observer to load the map when it enters the viewport
177          // It will be used to handle map loading instead of displaying the map on page load.
178          let mapObserver = null;
179          if ('IntersectionObserver' in window) {
180            mapObserver = new IntersectionObserver(function(entries, observer) {
181              for (let i = 0; i < entries.length; i++) {
182                if (entries[i].isIntersecting) {
183                  const mapid = entries[i].target.id;
184                  loadMap(mapid);
185                }
186              }
187            });
188          }
189
190          // Load the Leaflet Map, lazy based on the mapObserver, or not.
191          if (mapObserver && leaflet_settings.map.settings.map_lazy_load?.lazy_load) {
192            mapObserver.observe(this);
193          } else {
194            loadMap(mapid);
195          }
196        });
197      });
198    }
199  };
200
201  /**
202   * Define a main Drupal.Leaflet function being generated/triggered on each
203   * Leaflet Map map_container element load.
204   *
205   * @param map_container
206   *   The Leaflet Map map_container.
207   * @param mapid
208   *   The Leaflet Map id.
209   * @param map_definition
210   *   The Leaflet Map definition.
211   * @constructor
212   */
213  Drupal.Leaflet = function(map_container, mapid, map_definition) {
214    this.mapid = mapid;
215    this.map_definition = map_definition;
216    this.map_settings = this.map_definition.settings;
217    this.bounds = [];
218    this.base_layers = {};
219    this.overlays = {};
220    this.lMap = null;
221    this.start_center = null;
222    this.start_zoom = null;
223    this.layer_control = null;
224    this.markers = {};
225    this.features = {};
226    this.initialise(mapid);
227  };
228
229  /**
230   * Initialise the specific Leaflet Map
231   *
232   * @param mapid
233   *   The dom element #id to inject the Leaflet Map into.
234   */
235  Drupal.Leaflet.prototype.initialise = function(mapid) {
236    // Instantiate a new Leaflet map.
237    this.lMap = new L.Map(mapid, this.map_settings);
238
239    // Add map layers (base and overlay layers).
240    let i = 0;
241    for (const key in this.map_definition.layers) {
242      const layer = this.map_definition.layers[key];
243      // Distinguish between "base" and "overlay" layers.
244      // Default to "base" in case "layer_type" has not been defined in hook_leaflet_map_info().
245      layer.layer_type = (typeof layer.layer_type === 'undefined') ? 'base' : layer.layer_type;
246
247      switch (layer.layer_type) {
248        case 'overlay':
249          const overlay_layer = this.create_layer(layer, key);
250          const layer_hidden = (typeof layer.layer_hidden === "undefined") ? false : layer.layer_hidden;
251          this.add_overlay(key, overlay_layer, layer_hidden);
252          break;
253
254        default:
255          this.add_base_layer(key, layer, i);
256          // Only the first base layer needs to be added to the map - all the
257          // others are accessed via the layer switcher.
258          if (i === 0) {
259            i++;
260          }
261          break;
262      }
263      i++;
264    }
265
266    // Set initial view, fallback to displaying the whole world.
267    if (this.map_settings.center && this.map_settings.zoom) {
268      this.lMap.setView(
269        new L.LatLng(this.map_settings.center.lat, this.map_settings.center.lon),
270        this.map_settings.zoom
271      );
272    }
273    else {
274      this.lMap.fitWorld();
275    }
276
277    // Set the position of the Zoom Control, if enabled.
278    if (this.lMap.zoomControl) {
279      this.lMap.zoomControl.setPosition(this.map_settings.zoomControlPosition);
280    }
281
282    // Set to refresh when first in viewport to avoid missing visibility.
283    new IntersectionObserver((entries, observer) => {
284      entries.forEach(entry => {
285        if (entry.intersectionRatio > 0) {
286          this.lMap.invalidateSize();
287          observer.disconnect();
288        }
289      });
290    }).observe(this.lMap._container);
291  };
292
293  /**
294   * Initialise the Leaflet Map Layers (Overlays) Control
295   */
296  Drupal.Leaflet.prototype.initialise_layers_control = function() {
297    const count_layers = function(obj) {
298      // Browser compatibility: Chrome, IE 9+, FF 4+, or Safari 5+.
299      // @see http://kangax.github.com/es5-compat-table/
300      return Object.keys(obj).length;
301    };
302
303    // Only add a layer switcher if it is enabled in settings, and we have
304    // at least two base layers or at least one overlay.
305    if (this.layer_control == null &&
306        ((this.map_settings.layerControl && count_layers(this.base_layers) > 1) ||
307         count_layers(this.overlays) > 0)) {
308      const base_layers = count_layers(this.base_layers) > 1 ? this.base_layers : [];
309      // Instantiate layer control, using settings.layerControl as settings.
310      this.layer_control = new L.Control.Layers(
311        base_layers,
312        [],
313        this.map_settings.layerControlOptions
314      );
315    }
316  };
317
318  /**
319   * Create & Add a Base Layer to the Leaflet Map and Layers Control.
320   *
321   * @param key
322   *   The Layer index/label.
323   * @param definition
324   *   The Layer definition,
325   * @param i
326   *   The layers progressive counter.
327   */
328  Drupal.Leaflet.prototype.add_base_layer = function(key, definition, i) {
329    const base_layer = this.create_layer(definition, key);
330    this.base_layers[key] = base_layer;
331
332    // Only the first base layer needs to be added to the map - all the others are accessed via the layer switcher.
333    if (i === 0) {
334      this.lMap.addLayer(base_layer);
335    }
336
337    // Initialise the Layers Control, if not yet.
338    if (this.layer_control == null) {
339      this.initialise_layers_control();
340    } else {
341      // Add the new base layer to layer_control.
342      this.layer_control.addBaseLayer(base_layer, key);
343    }
344  };
345
346  /**
347   * Adds a Specific Layer and related Overlay to the Leaflet Map.
348   *
349   * @param {string} label
350   *   The Overlay Layer Label.
351   * @param layer
352   *   The Leaflet Overlay.
353   * @param {boolean} hidden_layer
354   *   The flag to disable the Layer from the Over Layers Control.
355   */
356  Drupal.Leaflet.prototype.add_overlay = function(label, layer, hidden_layer) {
357    if (!hidden_layer) {
358      this.lMap.addLayer(layer);
359    }
360
361    // Add the Overlay to the Drupal.Leaflet object.
362    this.overlays[label] = layer;
363
364    // Initialise the Layers Control, if it is not.
365    if (label && this.layer_control == null) {
366      this.initialise_layers_control();
367    }
368
369    // Add the Overlay to the Layer Control only if there is a Label.
370    if (label && this.layer_control) {
371      // If we already have a layer control, add the new overlay to it.
372      this.layer_control.addOverlay(layer, label);
373    }
374  };
375
376  /**
377   * Add Leaflet Features to the Leaflet Map.
378   *
379   * @param {Array} features
380   *   Features List definition.
381   * @param {boolean} initial
382   *   Boolean to identify initial status.
383   */
384  Drupal.Leaflet.prototype.add_features = function(features, initial) {
385    // Define Map Layers holder.
386    const layers = {};
387
388    for (let i = 0; i < features.length; i++) {
389      const feature = features[i];
390      let lFeature;
391
392      // In case of a Features Group.
393      if (feature.group) {
394        // Define a named Layer Group
395        layers[feature.group_label] = this.create_feature_group();
396        for (const groupKey in feature.features) {
397          const groupFeature = feature.features[groupKey];
398          lFeature = this.create_feature(groupFeature);
399          if (lFeature !== undefined) {
400            // Add the lFeature to the lGroup.
401            layers[feature.group_label].addLayer(lFeature);
402
403            // Allow others to do something with the feature that was just added to the map.
404            $(document).trigger('leaflet.feature', [lFeature, groupFeature, this, layers]);
405          }
406        }
407
408        // Add the group to the layer switcher.
409        this.add_overlay(feature.group_label, layers[feature.group_label], feature.disabled);
410      }
411      else {
412        lFeature = this.create_feature(feature);
413        if (lFeature !== undefined) {
414          // Add the Leaflet Feature to the Map.
415          this.lMap.addLayer(lFeature);
416
417          // Allow others to do something with the feature that was just added to the map.
418          $(document).trigger('leaflet.feature', [lFeature, feature, this]);
419        }
420      }
421    }
422
423    // Allow plugins to do things after features have been added.
424    $(document).trigger('leaflet.features', [initial || false, this]);
425  };
426
427  /**
428   * Create a Leaflet Feature Group.
429   *
430   * @returns {Object}
431   *   A Leaflet feature group.
432   */
433  Drupal.Leaflet.prototype.create_feature_group = function() {
434    return new L.featureGroup();
435  };
436
437  /**
438   * Add Leaflet Popup to the Leaflet Feature.
439   *
440   * @param {Object} lFeature
441   *   The Leaflet Feature.
442   * @param {Object} feature
443   *   The Feature coming from Drupal settings.
444   */
445  Drupal.Leaflet.prototype.feature_bind_popup = function(lFeature, feature) {
446    // Attach the Popup only if supported and a value is set for it.
447    if (typeof lFeature.bindPopup !== "undefined" && feature.popup && feature.popup.value) {
448      const popup_options = feature.popup.options ? JSON.parse(feature.popup.options) : {};
449      lFeature.bindPopup(feature.popup.value, popup_options);
450    }
451  };
452
453  /**
454   * Add Leaflet Tooltip to the Leaflet Feature.
455   *
456   * @param {Object} lFeature
457   *   The Leaflet Feature.
458   * @param {Object} feature
459   *   The Feature coming from Drupal settings.
460   */
461  Drupal.Leaflet.prototype.feature_bind_tooltip = function(lFeature, feature) {
462    // Set the Leaflet Tooltip, with its options (if the stripped value is not null).
463    if (feature.tooltip && feature.tooltip.value.replace(/(<([^>]+)>)/gi, "").trim().length > 0) {
464      const tooltip_options = feature.tooltip.options ? JSON.parse(feature.tooltip.options) : {};
465
466      // Cleanup tooltip className from commas, to support multivalue class fields.
467      if (tooltip_options.hasOwnProperty('className') && tooltip_options.className.length > 0) {
468        tooltip_options.className = tooltip_options.className.replaceAll(",", "");
469      }
470
471      // Need to more correctly set the tooltip_options.permanent option.
472      tooltip_options.permanent = tooltip_options.permanent === true || tooltip_options.permanent === "true";
473
474      lFeature.bindTooltip(feature.tooltip.value, tooltip_options);
475    }
476  };
477
478  /**
479   * Set Leaflet Feature path style.
480   *
481   * @param {Object} lFeature
482   *   The Leaflet Feature.
483   * @param {Object} feature
484   *   The Feature coming from Drupal settings.
485   */
486  Drupal.Leaflet.prototype.set_feature_path_style = function(lFeature, feature) {
487    let lFeature_path_style;
488    try {
489      lFeature_path_style = feature.path ?
490        (feature.path instanceof Object ? feature.path : JSON.parse(feature.path)) : {};
491    }
492    catch (e) {
493      lFeature_path_style = {};
494    }
495
496    // Make sure that the weight property is cast into integer, for avoiding
497    // polygons eventually disappearing with pan and zooming.
498    // @see: https://stackoverflow.com/a/65892728/5451394
499    if (lFeature_path_style.hasOwnProperty('weight')) {
500      lFeature_path_style.weight = parseInt(lFeature_path_style.weight);
501    }
502    lFeature.setStyle(lFeature_path_style);
503  };
504
505  /**
506   * Extend Map Bounds with new lFeature/feature.
507   *
508   * @param {Object} lFeature
509   *   The Leaflet Feature.
510   * @param {Object} feature
511   *   The Feature coming from Drupal settings.
512   *   (this parameter should be kept to eventually extend this method with
513   *   conditional logics on feature properties)
514   */
515  Drupal.Leaflet.prototype.extend_map_bounds = function(lFeature, feature) {
516    if (feature.type === 'point') {
517      this.bounds.push([feature.lat, feature.lon]);
518    } else if (lFeature.getBounds) {
519      this.bounds.push(lFeature.getBounds().getSouthWest(), lFeature.getBounds().getNorthEast());
520    }
521  };
522
523  /**
524   * Add Marker and Feature to the Drupal.Leaflet object.
525   *
526   * @param {Object} lFeature
527   *   The Leaflet Feature.
528   * @param {Object} feature
529   *   The Feature coming from Drupal settings.
530   */
531  Drupal.Leaflet.prototype.push_markers_features = function(lFeature, feature) {
532    if (feature.entity_id) {
533      // Generate the markers object index based on entity id (and geofield
534      // cardinality), and add the marker to the markers object.
535      const entity_id = feature.entity_id;
536      if (this.map_definition.geofield_cardinality && this.map_definition.geofield_cardinality !== 1) {
537        let i = 0;
538        while (this.markers[entity_id + '-' + i]) {
539          i++;
540        }
541        this.markers[entity_id + '-' + i] = lFeature;
542        this.features[entity_id + '-' + i] = feature;
543      }
544      else {
545        this.markers[entity_id] = lFeature;
546        this.features[entity_id] = feature;
547      }
548    }
549  };
550
551  /**
552   * Generates a Leaflet Geometry (Point or Geometry).
553   *
554   * @param {Object} feature
555   *   The feature definition coming from Drupal backend.
556   * @param {Object|boolean} map_settings
557   *   The map_settings if defined, false otherwise.
558   *
559   * @returns {Object}
560   *   The generated Leaflet Geometry.
561   */
562  Drupal.Leaflet.prototype.create_geometry = function(feature, map_settings = false) {
563    let lFeature;
564    switch (feature.type) {
565      case 'point':
566        lFeature = this.create_point(feature);
567        break;
568
569      case 'linestring':
570        lFeature = this.create_linestring(
571          feature,
572          map_settings?.leaflet_markercluster.include_path ?? false
573        );
574        break;
575
576      case 'polygon':
577        lFeature = this.create_polygon(
578          feature,
579          map_settings?.leaflet_markercluster.include_path ?? false
580        );
581        break;
582
583      case 'multipolygon':
584        lFeature = this.create_multipolygon(
585          feature,
586          map_settings?.leaflet_markercluster.include_path ?? false
587        );
588        break;
589
590      case 'multipolyline':
591        lFeature = this.create_multipoly(
592          feature,
593          map_settings?.leaflet_markercluster.include_path ?? false
594        );
595        break;
596
597      // In case of singular cases where feature.type is json we use this.create_json method.
598      // @see https://www.drupal.org/project/leaflet/issues/3377403
599      // @see https://www.drupal.org/project/leaflet/issues/3186029
600      case 'json':
601        lFeature = this.create_json(feature.json, feature.options, feature.events);
602        break;
603
604      case 'multipoint':
605      case 'geometrycollection':
606        lFeature = this.create_collection(feature);
607        break;
608
609      default:
610        lFeature = {};
611    }
612    return lFeature;
613  };
614
615  /**
616   * Generates a Leaflet Feature (Point or Geometry)
617   * with Leaflet adds on (Tooltip, Popup, Path Styles, etc.)
618   *
619   * @param {Object} feature
620   *   The feature definition coming from Drupal backend.
621   * @returns {Object}
622   *   The generated Leaflet Feature.
623   */
624  Drupal.Leaflet.prototype.create_feature = function(feature) {
625    const map_settings = this.map_settings ?? null;
626    const lFeature = this.create_geometry(feature, map_settings);
627
628    // Eventually add Tooltip to the lFeature.
629    this.feature_bind_tooltip(lFeature, feature);
630
631    // Eventually add Popup to the lFeature.
632    this.feature_bind_popup(lFeature, feature);
633
634    // Eventually Set Style for Path/Geometry lFeature.
635    if (lFeature.setStyle) {
636      this.set_feature_path_style(lFeature, feature);
637    }
638
639    // Eventually extend Map Bounds with new lFeature.
640    this.extend_map_bounds(lFeature, feature);
641
642    // Add Marker and Feature to the Drupal.Leaflet object.
643    this.push_markers_features(lFeature, feature);
644
645    return lFeature;
646  };
647
648  /**
649   * Generate a Leaflet Layer.
650   *
651   * @param {Object} layer
652   *   The Layer definition.
653   * @param {string} key
654   *   The Layer index/label.
655   *
656   * @returns {Object}
657   *   The Leaflet layer object.
658   */
659  Drupal.Leaflet.prototype.create_layer = function(layer, key) {
660    const self = this;
661    let map_layer;
662    const layer_type = layer.type ?? 'base';
663    const urlTemplate = layer.urlTemplate ?? '';
664    const layer_options = layer.options ?? {};
665
666    switch (layer_type) {
667      case 'wms':
668        map_layer = new L.tileLayer.wms(urlTemplate, layer_options);
669        break;
670
671      case 'vector':
672        map_layer = new L.maplibreGL({
673          style: urlTemplate,
674          attribution: layer_options.attribution ?? '',
675          pitch: layer_options.pitch ?? '',
676          bearing: layer_options.bearing ?? ''
677        });
678        break;
679
680      default:
681        map_layer = new L.tileLayer(urlTemplate, layer_options);
682    }
683
684    map_layer._leaflet_id = key;
685
686    // Layers served from TileStream need this correction in the y coordinates.
687    // TODO: Need to explore this more and find a more elegant solution.
688    if (layer.type === 'tilestream') {
689      map_layer.getTileUrl = function(tilePoint) {
690        self._adjustTilePoint(tilePoint);
691        const zoom = self._getZoomForUrl();
692        return L.Util.template(self._url, L.Util.extend({
693          s: self._getSubdomain(tilePoint),
694          z: zoom,
695          x: tilePoint.x,
696          y: Math.pow(2, zoom) - tilePoint.y - 1
697        }, self.options));
698      };
699    }
700    return map_layer;
701  };
702
703  /**
704   * Leaflet Icon creator.
705   *
706   * @param {Object} options
707   *   The Icon options.
708   *
709   * @returns {Object}
710   *   Leaflet Icon object.
711   */
712  Drupal.Leaflet.prototype.create_icon = function(options) {
713    const icon_options = {
714      iconUrl: options.iconUrl,
715    };
716
717    // Apply Icon properties
718    // @see https://leafletjs.com/reference.html#icon
719
720    // Icon Size.
721    if (options.iconSize) {
722      icon_options.iconSize = new L.Point(
723        parseInt(options.iconSize.x),
724        parseInt(options.iconSize.y)
725      );
726    }
727
728    // Icon Anchor.
729    if (options.iconAnchor && options.iconAnchor.x && options.iconAnchor.y) {
730      icon_options.iconAnchor = new L.Point(
731        parseInt(options.iconAnchor.x),
732        parseInt(options.iconAnchor.y)
733      );
734    }
735
736    // Popup Anchor.
737    if (options.popupAnchor && options.popupAnchor.x && options.popupAnchor.y) {
738      icon_options.popupAnchor = new L.Point(
739        parseInt(options.popupAnchor.x),
740        parseInt(options.popupAnchor.y)
741      );
742    }
743
744    // Popup ShadowUrl.
745    if (options.shadowUrl) {
746      icon_options.shadowUrl = options.shadowUrl;
747    }
748
749    // Popup ShadowSize.
750    if (options.shadowSize && options.shadowSize.x && options.shadowSize.y) {
751      icon_options.shadowSize = new L.Point(
752        parseInt(options.shadowSize.x),
753        parseInt(options.shadowSize.y)
754      );
755    }
756
757    // Popup ShadowAnchor.
758    if (options.shadowAnchor && options.shadowAnchor.x && options.shadowAnchor.y) {
759      icon_options.shadowAnchor = new L.Point(
760        parseInt(options.shadowAnchor.x),
761        parseInt(options.shadowAnchor.y)
762      );
763    }
764
765    if (options.className) {
766      icon_options.className = options.className;
767    }
768
769    // Popup IconRetinaUrl.
770    // @see https://www.drupal.org/project/leaflet/issues/3268023
771    if (options.iconRetinaUrl) {
772      icon_options.iconRetinaUrl = options.iconRetinaUrl;
773    }
774
775    return new L.Icon(icon_options);
776  };
777
778  /**
779   * Leaflet DIV Icon creator.
780   *
781   * @param options
782   *   The Icon options.
783   *
784   * @returns {*}
785   */
786  Drupal.Leaflet.prototype.create_divicon = function (options) {
787    let html_class = options['html_class'] || '';
788    let icon = new L.DivIcon({html: options.html, className: html_class});
789
790    // Apply Icon properties
791    // @see https://leafletjs.com/reference.html#icon
792
793    // Icon Size.
794    if (options.iconSize) {
795      icon.options.iconSize = new L.Point(parseInt(options.iconSize.x, 10), parseInt(options.iconSize.y, 10));
796    }
797    // Icon Anchor.
798    if (options.iconAnchor && options.iconAnchor.x && options.iconAnchor.y) {
799      icon.options.iconAnchor = new L.Point(parseInt(options.iconAnchor.x), parseInt(options.iconAnchor.y));
800    }
801    // Popup Anchor.
802    if (options.popupAnchor && options.popupAnchor.x && options.popupAnchor.y) {
803      icon.options.popupAnchor = new L.Point(parseInt(options.popupAnchor.x), parseInt(options.popupAnchor.y));
804    }
805
806    return icon;
807  };
808
809  /**
810   * Leaflet Point (Marker) creator.
811   *
812   * @param feature
813   *   The feature definition.
814   *
815   * @returns {*}
816   */
817  Drupal.Leaflet.prototype.create_point = function(feature) {
818
819    const latLng = new L.LatLng(feature.lat, feature.lon);
820    let lMarker;
821    // Assign the marker title value depending if a Marker simple title or a
822    // Leaflet tooltip was set.
823    let marker_title = '';
824    if (feature.title) {
825      marker_title = feature.title.replace(/<[^>]*>/g, '').trim()
826    }
827    else if (feature.tooltip && feature.tooltip.value) {
828      marker_title = feature.tooltip.value.replace(/<[^>]*>/g, '').trim();
829    }
830    let options = {
831      // Define the title (as mouse hover tooltip) only in case the Leaflet Tooltip is not defined.
832      title: feature.title ? marker_title : "",
833      className: feature.className ? feature.className.replaceAll(",", "") : '',
834      alt: marker_title,
835      group_label: feature.group_label ?? '',
836    };
837
838    lMarker = new L.Marker(latLng, options);
839
840    if (feature.icon) {
841      if (feature.icon.iconType && feature.icon.iconType === 'html' && feature.icon.html) {
842        let icon = this.create_divicon(feature.icon);
843        lMarker.setIcon(icon);
844      }
845      else if (feature.icon.iconType && feature.icon.iconType === 'circle_marker') {
846        try {
847          options = feature.icon.circle_marker_options ? JSON.parse(feature.icon.circle_marker_options) : {};
848          options.radius = options.radius ? parseInt(options['radiu
848s']) : 10;
849        }
850        catch (e) {
851          options = {};
852        }
853        lMarker = new L.CircleMarker(latLng, options);
854      }
855      else if (feature.icon.iconUrl) {
856        feature.icon.iconSize = feature.icon.iconSize || {};
857        feature.icon.iconSize.x = feature.icon.iconSize.x || this.naturalWidth;
858        feature.icon.iconSize.y = feature.icon.iconSize.y || this.naturalHeight;
859        if (feature.icon.shadowUrl) {
860          feature.icon.shadowSize = feature.icon.shadowSize || {};
861          feature.icon.shadowSize.x = feature.icon.shadowSize.x || this.naturalWidth;
862          feature.icon.shadowSize.y = feature.icon.shadowSize.y || this.naturalHeight;
863        }
864        let icon = this.create_icon(feature.icon);
865        lMarker.setIcon(icon);
866      }
867    }
868
869    return lMarker;
870  };
871
872  /**
873   * Leaflet Linestring creator.
874   *
875   * @param polyline
876   *   The Polyline definition.
877   * @param clusterable
878   *   Clusterable bool option.
879   *
880   * @returns {*}
881   */
882  Drupal.Leaflet.prototype.create_linestring = function(polyline, clusterable = false) {
883    let latlngs = [];
884    for (let i = 0; i < polyline.points.length; i++) {
885      let latlng = new L.LatLng(polyline.points[i].lat, polyline.points[i].lon);
886      latlngs.push(latlng);
887    }
888    return clusterable ? new L.PolylineClusterable(latlngs) : new L.Polyline(latlngs);
889  };
890
891  /**
892   *  Leaflet Collection creator.
893   *
894   * @param collection
895   *   The collection definition.
896   *
897   * @returns {*}
898   */
899  Drupal.Leaflet.prototype.create_collection = function(collection) {
900    let layers = new L.featureGroup();
901    for (let x = 0; x < collection.component.length; x++) {
902      let feature = { ...collection, ...collection.component[x]};
903      layers.addLayer(this.create_feature(feature));
904    }
905    return layers;
906  };
907
908  /**
909   * Leaflet Polygon creator.
910   *
911   * @param polygon
912   *   The polygon definition,
913   * @param clusterable
914   *   Clusterable bool option.
915   *
916   * @returns {*}
917   */
918  Drupal.Leaflet.prototype.create_polygon = function(polygon, clusterable = false) {
919    const coordinates = polygon.points ?? [];
920    return clusterable ? new L.PolygonClusterable(coordinates) : new L.Polygon(coordinates);
921  };
922
923  /**
924   * Leaflet Multi-Polygon creator.
925   *
926   * @param multipolygon
927   *   The polygon definition,
928   * @param clusterable
929   *   Clusterable bool option.
930   *
931   * @returns {*}
932   */
933  Drupal.Leaflet.prototype.create_multipolygon = function(multipolygon, clusterable = false) {
934    const coordinates = multipolygon.points ?? [];
935    return clusterable ? new L.PolygonClusterable(coordinates) : new L.Polygon(coordinates);
936  };
937
938  /**
939   * Leaflet Multi-Poly creator (both Polygons & Poly-lines)
940   *
941   * @param multipoly
942   *   The multipoly definition,
943   * @param clusterable
944   *   Clusterable bool option.
945   *
946   * @returns {*}
947   */
948  Drupal.Leaflet.prototype.create_multipoly = function(multipoly, clusterable = false) {
949    let polygons = [];
950    for (let x = 0; x < multipoly.component.length; x++) {
951      let latlngs = [];
952      let polygon = multipoly.component[x];
953      for (let i = 0; i < polygon.points.length; i++) {
954        let latlng = new L.LatLng(polygon.points[i].lat, polygon.points[i].lon);
955        latlngs.push(latlng);
956      }
957      polygons.push(latlngs);
958    }
959    if (multipoly.multipolyline) {
960      return clusterable ? new L.PolylineClusterable(polygons) : new L.Polyline(polygons);
961    }
962    else {
963      return clusterable ? new L.PolygonClusterable(polygons) : new L.Polygon(polygons);
964    }
965  };
966
967  /**
968   * Leaflet Geo JSON Creator.
969   *
970   * In case of singular cases where feature type is json we use this.create_json method.
971   * @see https://www.drupal.org/project/leaflet/issues/3377403
972   * @see https://www.drupal.org/project/leaflet/issues/3186029
973   *
974   * @param json
975   *   The json input.
976   * @param options
977   *   The options array,
978   *   that would reflect the GeoJSON Leaflet Js library options
979   *   https://leafletjs.com/reference.html#geojson
980   * @param events
981   *   The events array
982   *
983   * @returns {*}
984   */
985  Drupal.Leaflet.prototype.create_json = function(json, options = [], events = []) {
986    let lJSON = new L.GeoJSON();
987    const self = this;
988
989    lJSON.options.onEachFeature = function(feature, layer) {
990      for (let layer_id in layer._layers) {
991        for (let i in layer._layers[layer_id]._latlngs) {
992        }
993      }
994      if (feature.properties.style) {
995        layer.setStyle(feature.properties.style);
996      }
997      if (feature.properties.leaflet_id) {
998        layer._leaflet_id = feature.properties.leaflet_id;
999      }
1000
1001      // Eventually add Tooltip to the lFeature.
1002      self.feature_bind_tooltip(layer, feature.properties);
1003
1004      // Eventually add Popup to the Layer.
1005      self.feature_bind_popup(layer, feature.properties);
1006
1007      for (const e in events) {
1008        let layerParam = {};
1009        layerParam[e] = eval(events[e]);
1010        layer.on(layerParam);
1011      }
1012    };
1013
1014    for (const option in options) {
1015      if (Object.prototype.hasOwnProperty.call(options, option)) {
1016        lJSON.options[option] = eval(options[option]);
1017      }
1018    }
1019
1020    lJSON.addData(json);
1021    return lJSON;
1022  };
1023
1024
1025  // Set Map initial map position and Zoom. Different scenarios:
1026  //  1)  Force the initial map center and zoom to values provided by input settings
1027  //  2)  Fit multiple features onto map using Leaflet's fitBounds method
1028  //  3)  Fit a single polygon onto map using Leaflet's fitBounds method
1029  //  4)  Display a single marker using the specified zoom
1030  //  5)  Adjust the initial zoom using zoomFiner, if specified
1031  //  6)  Cater for a map with no features (use input settings for Zoom and Center, if supplied)
1032  //
1033  // @NOTE: This method used by Leaflet Markecluster module (don't remove/rename)
1034  Drupal.Leaflet.prototype.fitBounds = function() {
1035    let start_zoom = this.map_settings.zoom ? this.map_settings.zoom : 12;
1036    // Note: this.map_settings.center might not be defined in case of Leaflet widget and Automatically locate user current position.
1037    let start_center = this.map_settings.center ? new L.LatLng(this.map_settings.center.lat, this.map_settings.center.lon) : new L.LatLng(0,0);
1038
1039    //  Check whether the Zoom and Center are to be forced to use the input settings
1040    if (this.map_settings.map_position_force) {
1041      //  Set the Zoom and Center to values provided by the input settings
1042      this.lMap.setView(start_center, start_zoom);
1043    } else {
1044      if (this.bounds.length === 0) {
1045        //  No features - set the Zoom and Center to values provided by the input settings, if specified
1046        this.lMap.setView(start_center, start_zoom);
1047      } else {
1048        //  Set the Zoom and Center by using the Leaflet fitBounds function
1049        const bounds = new L.LatLngBounds(this.bounds);
1050        const fitbounds_options = this.map_settings.fitbounds_options ? JSON.parse(this.map_settings.fitbounds_options) : {};
1051        this.lMap.fitBounds(bounds, fitbounds_options);
1052        start_center = bounds.getCenter();
1053        start_zoom = this.lMap.getBoundsZoom(bounds);
1054
1055        if (this.bounds.length === 1) {
1056          //  Single marker - set zoom to input settings
1057          this.lMap.setZoom(this.map_settings.zoom);
1058          start_zoom = this.map_settings.zoom;
1059        }
1060      }
1061
1062      // In case of map initial position not forced, and zooFiner not null/neutral,
1063      // adapt the Map Zoom and the Start Zoom accordingly.
1064      if (this.map_settings.hasOwnProperty('zoomFiner') && parseInt(this.map_settings.zoomFiner)) {
1065        start_zoom += parseFloat(this.map_settings.zoomFiner);
1066        this.lMap.setView(start_center, start_zoom);
1067      }
1068
1069      // Set the map start zoom and center.
1070      this.start_zoom = start_zoom;
1071      this.start_center = start_center;
1072    }
1073
1074  };
1075
1076  /**
1077   * Triggers a Leaflet Map Reset View action.
1078   *
1079   * @param mapid
1080   *   The Map identifier, to apply the rest to.
1081   */
1082  Drupal.Leaflet.prototype.map_reset = function(mapid) {
1083    Drupal.Leaflet[mapid].reset_view_control._resetView();
1084  };
1085
1086  // Extend the L.Polyline to make it clustarable.
1087  // @see https://gis.stackexchange.com/questions/197882/is-it-possible-to-cluster-polygons-in-leaflet
1088  L.PolylineClusterable = L.Polyline.extend({
1089    _originalInitialize: L.Polyline.prototype.initialize,
1090
1091    initialize: function (bounds, options) {
1092      this._originalInitialize(bounds, options);
1093      this._latlng = this.getBounds().getCenter();
1094    },
1095
1096    getLatLng: function () {
1097      return this._latlng;
1098    },
1099
1100    setLatLng: function () {}
1101  });
1102
1103  // Extend the L.Polygon to make it clustarable.
1104  // @see https://gis.stackexchange.com/questions/197882/is-it-possible-to-cluster-polygons-in-leaflet
1105  L.PolygonClusterable = L.Polygon.extend({
1106    _originalInitialize: L.Polygon.prototype.initialize,
1107
1108    initialize: function (bounds, options) {
1109      this._originalInitialize(bounds, options);
1110      this._latlng = this.getBounds().getCenter();
1111    },
1112
1113    getLatLng: function () {
1114      return this._latlng;
1115    },
1116
1117    setLatLng: function () {}
1118  });
1119
1120})(jQuery, 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.