PageSourceSearch

https://www.faurecia-polska.pl/modules/contrib/geofield_map/js/geofield_map_gmap_formatter.js?v=1.x

js faurecia-polska.pl collected 2026-10-07 19:49:47 UTC 24,063 bytes, 592 lines download raw bytes

1/**
2 * @file
3 * Javascript for the Geofield Google Map formatter.
4 */
5
6(function ($, Drupal) {
7
8  'use strict';
9
10  /**
11   * The Geofield Google Map formatter behavior.
12   *
13   * @type {Drupal~behavior}
14   *
15   * @prop {Drupal~behaviorAttach} attach
16   *   Attaches the Geofield Google Map formatter behavior.
17   */
18  Drupal.behaviors.geofieldGoogleMap = {
19    attach: function (context, drupalSettings) {
20
21      function loadMap(mapId) {
22        let map_settings = drupalSettings['geofield_google_map'][mapId]['map_settings'];
23        let data = drupalSettings['geofield_google_map'][mapId]['data'];
24
25        // Set the map_data[mapid] settings.
26        Drupal.geoFieldMapFormatter.map_data[mapId] = map_settings;
27
28        // Load before the GMap Library, if needed.
29        Drupal.geoFieldMapFormatter.loadGoogle(mapId, map_settings.gmap_api_key, map_settings.map_additional_libraries, function () {
30          if (!document.getElementById(mapId)) {
31            return;
32          }
33          Drupal.geoFieldMapFormatter.map_initialize(mapId, map_settings, data, context);
34        });
35      }
36
37      if (drupalSettings['geofield_google_map']) {
38        // If the IntersectionObserver API is available, create an observer to load the map when it enters the viewport
39        // It will be used to handle map loading instead of displaying the map on page load.
40        let mapObserver = null;
41        if ('IntersectionObserver' in window){
42          mapObserver = new IntersectionObserver(function (entries, observer) {
43            for(var i = 0; i < entries.length; i++) {
44              if(entries[i].isIntersecting) {
45                once('geofield-map-loaded', entries[i].target).forEach(function (el) {
46                  loadMap(el.id);
47                });
48              }
49            }
50          });
51        }
52
53        once('geofield-processed', 'html .geofield-google-map').forEach(function (element) {
54          const mapId = $(element).attr('id');
55          if (drupalSettings['geofield_google_map'][mapId]) {
56            const map_settings = drupalSettings['geofield_google_map'][mapId]['map_settings'];
57            if (mapObserver && map_settings['map_lazy_load']['lazy_load']) {
58              mapObserver.observe(element)
59            } else {
60              loadMap(mapId);
61            }
62          }
63        });
64      }
65    }
66  };
67
68  Drupal.geoFieldMapFormatter = {
69
70    map_start: {
71      center: {lat: 41.85, lng: -87.65},
72      zoom: 18
73    },
74
75    map_data: {},
76
77    // Google Maps are loaded lazily. In some situations load_google() is
78    // called twice, which results in "You have included the Google Maps API
79    // multiple times on this page. This may cause unexpected errors." errors.
80    // This flag will prevent repeat $.getScript() calls.
81    maps_api_loading: false,
82
83    /**
84     * Returns the re-coded Google Maps api language parameter, from html lang attribute.
85     *
86     * @param {string} html_language - The language id string
87     *
88     * @return {string} - The transformed language id string
89     */
90    googleMapsLanguage: function (html_language) {
91      switch (html_language) {
92        case 'zh-hans':
93          html_language = 'zh-CN';
94          break;
95
96        case 'zh-hant':
97          html_language = 'zh-TW';
98          break;
99      }
100      return html_language;
101    },
102
103    /**
104     * Provides the callback that is called when maps loads.
105     */
106    googleCallback: function () {
107      let self = this;
108      // Wait until the window load event to try to use the Maps library.
109      $(document).ready(function (e) {
110        self.googleCallbacks.forEach(function (callback) {
111          callback.callback();
112        });
113        self.googleCallbacks = [];
114      });
115    },
116
117    /**
118     * Adds a callback that will be called once the maps library is loaded.
119     *
120     * @param {string} callback - The callback
121     */
122    addCallback: function (callback) {
123      let self = this;
124      // Ensure callbacks array.
125      self.googleCallbacks = self.googleCallbacks || [];
126      self.googleCallbacks.push({callback: callback});
127    },
128
129    /**
130     * Load Google Maps library.
131     *
132     * @param {string} mapid - The map id
133     * @param {string} gmap_api_key - The gmap api key
134     * @param {array} additional_libraries - Additional Libraries list
135     * @param {string} callback - the Callback function  name
136     */
137    loadGoogle: function (mapid, gmap_api_key, additional_libraries, callback) {
138      let self = this;
139      let html_language = $('html').attr('lang') || 'en';
140
141      // Add the callback.
142      self.addCallback(callback);
143
144      // Check for Google Maps.
145      if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
146        if (self.maps_api_loading === true) {
147          return;
148        }
149
150        self.maps_api_loading = true;
151        // Google Maps isn't loaded so lazy load Google Maps.
152        // Default script path.
153        let scriptPath = self.map_data[mapid]['gmap_api_localization'] + '?v=weekly&sensor=false&language=' + self.googleMapsLanguage(html_language) + '&callback=Drupal.geoFieldMapFormatter.googleCallback'  + '&loading=async';
154
155        // If a Google API key is set, use it.
156        if (gmap_api_key) {
157          scriptPath += '&key=' + gmap_api_key;
158        }
159
160        if (additional_libraries) {
161          let libraries = [];
162          for (let library in additional_libraries) {
163            if (additional_libraries.hasOwnProperty(library)) {
164              libraries.push(library);
165            }
166          }
167          scriptPath += '&libraries=' + libraries.join();
168        }
169
170        $.getScript(scriptPath)
171          .done(function () {
172            self.maps_api_loading = false;
173          });
174
175      }
176      else {
177        // Google Maps loaded. Run callback.
178        self.googleCallback();
179      }
180    },
181
182    checkImage: function (imageSrc, setIcon, logError) {
183      let img = new Image();
184      img.src = imageSrc;
185      img.onload = setIcon;
186      img.onerror = logError;
187    },
188
189    place_feature: function (feature, mapid) {
190      let self = this;
191      let icon_image = null;
192
193      // Override and set icon image with geojsonProperties.icon, if set as not
194      // null/empty.
195      if (feature.geojsonProperties.icon && feature.geojsonProperties.icon.length > 0) {
196        icon_image = feature.geojsonProperties.icon;
197      }
198
199      // Set the personalized Icon Image, if set.
200      if (feature.setIcon && icon_image && icon_image.length > 0) {
201        self.checkImage(icon_image,
202          // Success loading image.
203          function () {
204            feature.setIcon(icon_image);
205          });
206      }
207
208      // Add a default Tooltip on the title geojsonProperty, if existing.
209      if (feature.setTitle && feature.geojsonProperties.tooltip) {
210        feature.setTitle(feature.geojsonProperties.tooltip);
211      }
212
213      const map = self.map_data[mapid].map;
214
215      // If the feature is a Point, make it a Marker and extend the Map bounds.
216      if (feature.getPosition) {
217        // Define the OverlappingMarkerSpiderfier flag.
218        const oms = self.map_data[mapid].oms ? self.map_data[mapid].oms : null;
219        if (oms) {
220          self.map_data[mapid].oms.addMarker(feature);
221        }
222        else {
223          feature.setMap(map);
224        }
225
226        // Generate the markers object index based on entity id (and geofield
227        // cardinality), and add the marker to the markers object.
228        const entity_id = feature['geojsonProperties']['entity_id'];
229        if (self.map_data[mapid].geofield_cardinality && self.map_data[mapid].geofield_cardinality !== 1) {
230          let i = 0;
231          while (self.map_data[mapid].markers[entity_id + '-' + i]) {
232            i++;
233          }
234          self.map_data[mapid].markers[entity_id + '-' + i] = feature;
235        }
236        else {
237          self.map_data[mapid].markers[entity_id] = feature;
238        }
239
240        self.map_data[mapid].map_bounds.extend(feature.getPosition());
241
242        // Check for eventual simple or OverlappingMarkerSpiderfier click
243        // Listener.
244        const clickListener = oms ? 'spider_click' : 'click';
245        google.maps.event.addListener(feature, clickListener, function () {
246          self.infowindow_open(mapid, feature);
247        });
248      }
249
250      // If the feature is a Polyline or a Polygon, add to the Map and extend
251      // the Map bounds.
252      if (feature.getPath) {
253        let feature_options = feature.geojsonProperties.path_options ? JSON.parse(feature.geojsonProperties.path_options) : {};
254        feature.setOptions(feature_options);
255        feature.setMap(map);
256        let path = feature.getPath();
257        path.forEach(function (element) {
258          self.map_data[mapid].map_bounds.extend(element);
259        });
260        google.maps.event.addListener(feature, 'click', function (event) {
261          self.infowindow_open(mapid, feature, event.latLng);
262        });
263      }
264
265    },
266
267    // Closes and open the Map Infowindow at the input feature.
268    infowindow_open: function (mapid, feature, anchor) {
269      let self = this;
270      let map = self.map_data[mapid].map;
271      let properties = feature.get('geojsonProperties');
272      if (feature.setTitle && properties && properties.title) {
273        feature.setTitle(properties.title);
274      }
275      map.infowindow.close();
276      if (properties.description) {
277        map.infowindow.setContent(properties.description);
278
279        // Note: if the feature is a Marker (and not a Polyline/Polygon) its
280        // extensions will override the infowindow anchor position, in the map.
281        // infowindow.open method.
282        map.infowindow.setPosition(anchor);
283        setTimeout(function () {
284          map.infowindow.open(map, feature);
285        }, 200);
286      }
287    },
288
289    map_refresh: function (mapid) {
290      let self = this;
291      setTimeout(function () {
292        google.maps.event.trigger(self.map_data[mapid].map, 'resize');
293      }, 10);
294    },
295
296    // Init Geofield Google Map and its functions.
297    map_initialize: function (mapid, map_settings, data, context) {
298      let self = this;
299
300      // If google and google.maps have been defined.
301      if (google && google.maps) {
302        let styledMapType;
303
304        let mapOptions = {
305          center: map_settings.map_center ? new google.maps.LatLng(map_settings.map_center.lat, map_settings.map_center.lon) : new google.maps.LatLng(42, 12.5),
306          zoom: map_settings.map_zoom_and_pan.zoom.initial ? parseInt(map_settings.map_zoom_and_pan.zoom.initial) : 8,
307          minZoom: map_settings.map_zoom_and_pan.zoom.min ? parseInt(map_settings.map_zoom_and_pan.zoom.min) : 1,
308          maxZoom: map_settings.map_zoom_and_pan.zoom.max ? parseInt(map_settings.map_zoom_and_pan.zoom.max) : 20,
309          gestureHandling: map_settings.map_zoom_and_pan.gestureHandling || 'auto',
310          mapTypeId: map_settings.map_controls.map_type_id || 'roadmap'
311        };
312
313        // Manage the old scrollwheel & draggable settings (deprecated by Google Maps api).
314        if (!map_settings.map_zoom_and_pan.scrollwheel && !map_settings.map_zoom_and_pan.gestureHandling) {
315          mapOptions.scrollwheel = false;
316        }
317        if (!map_settings.map_zoom_and_pan.draggable && !map_settings.map_zoom_and_pan.gestureHandling) {
318          mapOptions.draggable = false;
319        }
320
321        if (map_settings.map_controls.disable_default_ui) {
322          mapOptions.disableDefaultUI = map_settings.map_controls.disable_default_ui;
323        }
324        else {
325          // Implement Custom Style Map, if Set.
326          if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_control && map_settings.custom_style_map.custom_style_name.length > 0 && map_settings.custom_style_map.custom_style_options.length > 0) {
327            let customMapStyleName = map_settings.custom_style_map.custom_style_name;
328            let customMapStyle = JSON.parse(map_settings.custom_style_map.custom_style_options);
329            styledMapType = new google.maps.StyledMapType(customMapStyle, {name: customMapStyleName});
330            map_settings.map_controls.map_type_control_options_type_ids.push('custom_styled_map');
331          }
332
333          mapOptions.zoomControl = !!map_settings.map_controls.zoom_control;
334          mapOptions.mapTypeControl = !!map_settings.map_controls.map_type_control;
335          mapOptions.mapTypeControlOptions = {
336            mapTypeIds: map_settings.map_controls.map_type_control_options_type_ids ? map_settings.map_controls.map_type_control_options_type_ids : ['roadmap', 'satellite', 'hybrid'],
337            position: google.maps.ControlPosition.TOP_LEFT
338          };
339          mapOptions.scaleControl = !!map_settings.map_controls.scale_control;
340          mapOptions.streetViewControl = !!map_settings.map_controls.street_view_control;
341          mapOptions.fullscreenControl = !!map_settings.map_controls.fullscreen_control;
342        }
343
344        // Add map_additional_options if any.
345        if (map_settings.map_additional_options.length > 0) {
346          let additionalOptions = JSON.parse(map_settings.map_additional_options);
347          // Transforms additionalOptions "true", "false" values into true &
348          // false.
349          for (let prop in additionalOptions) {
350            if (additionalOptions.hasOwnProperty(prop)) {
351              if (additionalOptions[prop] === 'true') {
352                additionalOptions[prop] = true;
353              }
354              if (additionalOptions[prop] === 'false') {
355                additionalOptions[prop] = false;
356              }
357            }
358          }
359          // Merge mapOptions with additionalOptions.
360          $.extend(mapOptions, additionalOptions);
361        }
362
363        // Define the Geofield Google Map.
364        let map = new google.maps.Map(document.getElementById(mapid), mapOptions);
365
366        // Add the Map Reset Control, if set.
367        if (map_settings.map_zoom_and_pan.map_reset) {
368          let mapResetControlPosition = map_settings.map_zoom_and_pan.map_reset_position || 'TOP_RIGHT';
369
370          // Create the DIV to hold the control and call the mapResetControl()
371          // constructor passing in this DIV.
372          let mapResetControlDiv = document.createElement('div');
373          mapResetControlDiv.style.zIndex = "10";
374          mapResetControlDiv.index = 1;
375          new self.map_reset_control(mapResetControlDiv, mapid);
376          map.controls[google.maps.ControlPosition[mapResetControlPosition]].push(mapResetControlDiv);
377        }
378
379        if (Drupal.geoFieldMapGeocoder && map_settings.map_geocoder.control) {
380          let mapGeocoderControlPosition = map_settings.map_geocoder.settings.position || 'TOP_RIGHT';
381          let mapGeocoderControlDiv = document.createElement('div');
382          Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control = new Drupal.geoFieldMapGeocoder.map_control(mapGeocoderControlDiv, mapid);
383          mapGeocoderControlDiv.index = 1;
384          map.controls[google.maps.ControlPosition[mapGeocoderControlPosition]].push(Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control);
385          Drupal.geoFieldMapGeocoder.map_control_autocomplete(mapid, map_settings.map_geocoder.settings, $(Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control), 'formatter', 'gmap');
386        }
387
388        // If defined a Custom Map Style, associate the styled map with
389        // custom_styled_map MapTypeId and set it to display.
390        if (styledMapType) {
391          map.mapTypes.set('custom_styled_map', styledMapType);
392          // Set Custom Map Style to Default, if requested.
393          if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_default) {
394            map.setMapTypeId('custom_styled_map');
395          }
396        }
397
398        // Define a mapid self property, so other code can interact with it.
399        self.map_data[mapid].map = map;
400        self.map_data[mapid].map_options = mapOptions;
401        self.map_data[mapid].features = data.features;
402        self.map_data[mapid].markers = {};
403
404        // Define the MapBounds property.
405        self.map_data[mapid].map_bounds = new google.maps.LatLngBounds();
406
407        // Set the zoom force and center property for the map.
408        self.map_data[mapid].zoom_force = !!map_settings.map_zoom_and_pan.zoom.force;
409        self.map_data[mapid].center_force = !!map_settings.map_center.center_force;
410
411        // Parse the Geojson data into Google Maps Locations.
412        let features = [];
413        for (let i in data.features) {
414          features[i] = Drupal.googleGeoJson(data.features[i]);
415        }
416
417        if (features.length > 0) {
418
419          /**
420           * Implement  OverlappingMarkerSpiderfier if its control set true.
421           */
422          if (map_settings.map_oms && map_settings.map_oms.map_oms_control && OverlappingMarkerSpiderfier) {
423            let omsOptions = map_settings.map_oms.map_oms_options.length > 0 ? JSON.parse(map_settings.map_oms.map_oms_options) : {
424              markersWontMove: true,
425              markersWontHide: true,
426              basicFormatEvents: true,
427              keepSpiderfied: true
428            };
429            self.map_data[mapid].oms = new OverlappingMarkerSpiderfier(map, omsOptions);
430          }
431
432          map.infowindow = new google.maps.InfoWindow({
433            content: ''
434          });
435
436          // If the map.infowindow is defined, add an event listener for the
437          // Ajax Infowindow Popup.
438          google.maps.event.addListener(map.infowindow, 'domready', function () {
439            let element = document.createElement('div');
440            element.innerHTML = map.infowindow.getContent().trim();
441            let content = $('[data-geofield-google-map-ajax-popup]', element);
442            if (content.length) {
443              let url = content.data('geofield-google-map-ajax-popup');
444              Drupal.ajax({url: url}).execute().done(function () {
445                Drupal.attachBehaviors(element, drupalSettings);
446              })
447            }
448          });
449
450          // Iterate on each features and place it to the map.
451          if (features.setMap) {
452            self.place_feature(features, mapid);
453          }
454          else {
455            for (let i in features) {
456              if (features[i].setMap) {
457                self.place_feature(features[i], mapid);
458              }
459              else {
460                for (let j in features[i]) {
461                  if (features[i][j].setMap) {
462                    self.place_feature(features[i][j], mapid);
463                  }
464                }
465              }
466            }
467          }
468
469          // Implement Markeclustering, if more than 1 marker on the map,
470          // and the markercluster option is set to true.
471          if (typeof MarkerClusterer !== 'undefined' && map_settings.map_markercluster.markercluster_control) {
472
473            let markeclusterOption = {
474              imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
475            };
476
477            // Add markercluster_additional_options if any.
478            if (map_settings.map_markercluster.markercluster_additional_options.length > 0) {
479              const markeclusterAdditionalOptions = JSON.parse(map_settings.map_markercluster.markercluster_additional_options);
480              // Merge markeclusterOption with markeclusterAdditionalOptions.
481              $.extend(markeclusterOption, markeclusterAdditionalOptions);
482            }
483
484            // Define a markerCluster property, so other code can interact with
485            // it.
486            let markerCluster = [];
487            const keys = Object.keys(self.map_data[mapid].markers);
488            for (let k = 0; k < keys.length; k++) {
489              markerCluster.push(self.map_data[mapid].markers[keys[k]]);
490            }
491            self.map_data[mapid].markerCluster = new MarkerClusterer(map, markerCluster, markeclusterOption);
492          }
493
494          // If the Map Initial State is defined by MapBounds,
495          // and the map center is not forced.
496          if (!self.mapBoundsAreNull(self.map_data[mapid].map_bounds) && !self.map_data[mapid].center_force) {
497            map.fitBounds(self.map_data[mapid].map_bounds);
498          }
499          // Else if the Map Initial State is defined by Markers in the exact
500          // same locations,.
501          else if (self.map_data[mapid].markers.constructor === Object &&
502            self.mapBoundsAreNull(self.map_data[mapid].map_bounds) &&
503            // And the map center is not forced.
504            !self.map_data[mapid].center_force) {
505            map.setCenter(self.map_data[mapid].markers[Object.keys(self.map_data[mapid].markers)[0]].getPosition());
506          }
507
508        }
509
510        google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
511          // Force the Map Zoom if requested.
512          if (self.map_data[mapid].zoom_force) {
513            self.map_data[mapid].map.setZoom(self.map_data[mapid].map_options.zoom);
514          }
515        });
516
517        // At the beginning (once) ...
518        google.maps.event.addListenerOnce(map, 'idle', function () {
519
520          // Open the Feature infowindow, if so set.
521          if (self.map_data[mapid].map_marker_and_infowindow.force_open) {
522            // map.setCenter(features[0].getPosition());
523            self.infowindow_open(mapid, features[0]);
524          }
525
526          // In case of map initial position not forced, and zooFiner not
527          // null/neutral, adapt the Map Zoom and the Start Zoom accordingly.
528          if (!self.map_data[mapid].zoom_force && self.map_data[mapid].map_zoom_and_pan.zoom.finer && self.map_data[mapid].map_zoom_and_pan.zoom.finer !== 0) {
529            map.setOptions({
530              zoom: map.getZoom() + parseInt(self.map_data[mapid].map_zoom_and_pan.zoom.finer)
531            });
532          }
533
534          // Update map initial state after everything is settled.
535          self.map_set_start_state(mapid, map.getCenter(), map.getZoom());
536
537          // Trigger a custom event on Geofield Map initialized, with mapid.
538          $(context).trigger('geofieldMapInit', mapid);
539        });
540
541      }
542    },
543
544    mapBoundsAreNull: function (mapBounds) {
545      let north_east = mapBounds.getNorthEast();
546      let south_west = mapBounds.getSouthWest();
547      return north_east.toString() === south_west.toString();
548
549    },
550
551    map_set_start_state: function (mapid, center, zoom) {
552      let self = this;
553      self.map_data[mapid].map_start_center = center;
554      self.map_data[mapid].map_start_zoom = zoom;
555    },
556
557    map_reset_control: function (controlDiv, mapid) {
558      // Set CSS for the control border.
559      let controlUI = document.createElement('div');
560      controlUI.style.backgroundColor = '#fff';
561      controlUI.style.boxShadow = 'rgba(0,0,0,.3) 0px 1px 4px -1px';
562      controlUI.style.cursor = 'pointer';
563      controlUI.title = Drupal.t('Click to reset the map to its initial state');
564      controlUI.style.margin = '10px';
565      controlUI.style.position = 'relative';
566      controlUI.id = 'geofield-map--' + mapid + '--reset-control';
567      controlDiv.appendChild(controlUI);
568
569      // Set CSS for the control interior.
570      let controlText = document.createElement('div');
571      controlText.style.position = 'relative';
572      controlText.innerHTML = Drupal.t('Reset Map');
573      controlText.style.padding = '0 17px';
574      controlText.style.display = 'table-cell';
575      controlText.style.height = '40px';
576      controlText.style.fontSize = '18px';
577      controlText.style.color = 'rgb(86,86,86)';
578      controlText.style.textAlign = 'center';
579      controlText.style.verticalAlign = 'middle';
580      controlUI.appendChild(controlText);
581
582      // Setup the click event listeners: simply set the map to Chicago.
583      controlUI.addEventListener('click', function () {
584        Drupal.geoFieldMapFormatter.map_data[mapid].map.setCenter(Drupal.geoFieldMapFormatter.map_data[mapid].map_start_center);
585        Drupal.geoFieldMapFormatter.map_data[mapid].map.setZoom(Drupal.geoFieldMapFormatter.map_data[mapid].map_start_zoom);
586      });
587      return controlUI;
588    }
589
590  };
591
592})(jQuery, Drupal);

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.