PageSourceSearch

https://survival.es/assets/controllers/numiko/multi_point_map_controller-821eaeda.js

js survival.es collected 2026-09-24 10:00:17 UTC 4,511 bytes, 141 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2import mapboxgl from "mapbox-gl"
3import supportsInteractionObserver from "utils/supports_interaction_observer"
4
5// Connects to data-controller="numiko--multi-point-map"
6export default class extends Controller {
7  static values = { token: String, style: String }
8
9  connect () {
10    if (supportsInteractionObserver()) {
11      this.#setupObserver();
12    } else {
13      this.#loadMapbox();
14    }
15  }
16
17  disconnect () {
18    if (this.intersectionObserver) {
19      this.intersectionObserver.disconnect();
20    }
21  }
22
23  #setupObserver () {
24    this.intersectionObserver = new window.IntersectionObserver(([entry]) => {
25      if (entry.isIntersecting) {
26        this.#loadMapbox();
27        return;
28      }
29    }, { root: null, threshold: 0.1 })
30
31    this.intersectionObserver.observe(this.element);
32  }
33
34  #loadMapbox () {
35    /* Should make file .js.erb and interpolate via ENV key */
36    mapboxgl.accessToken = this.tokenValue;
37    let mapData = document.getElementById('multi-point-map').dataset;
38    let center_coords = mapData.mapCenter.split(',').map(function(el) {
39      return parseFloat(el);
40    });
41    let map = new mapboxgl.Map({
42      container: 'multi-point-map',
43      style: this.styleValue,
44      center: center_coords,
45      maxBounds: [[-180.0, -80.0], [180.0, 80.0]],
46      zoom: mapData.mapZoom,
47      minZoom: 1,
48      scrollZoom: false,
49      dragRotate: false,
50      renderWorldCopies: false
51    });
52    let nav = new mapboxgl.NavigationControl();
53    map.addControl(nav, 'top-right');
54    return map.on('load', function() {
55      map.addSource('globalimpact', {
56        type: 'geojson',
57        data: mapData.mapDataUrl,
58        cluster: true,
59        clusterMaxZoom: 14,
60        clusterRadius: 50
61      });
62      map.addLayer({
63        id: 'clusters',
64        type: 'circle',
65        source: 'globalimpact',
66        filter: ['has', 'point_count'],
67        paint: {
68          'circle-color': ['step', ['get', 'point_count'], '#B33D26', 100, '#B33D26', 750, '#B33D26'],
69          'circle-radius': ['step', ['get', 'point_count'], 20, 10, 30, 20, 40, 30, 60]
70        }
71      });
72      map.addLayer({
73        id: 'cluster-count',
74        type: 'symbol',
75        source: 'globalimpact',
76        filter: ['has', 'point_count'],
77        layout: {
78          'text-field': '{point_count_abbreviated}',
79          'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
80          'text-size': 14
81        },
82        paint: {
83          'text-color': '#ffffff'
84        }
85      });
86      map.addLayer({
87        id: 'unclustered-point',
88        type: 'circle',
89        source: 'globalimpact',
90        filter: ['!', ['has', 'point_count']],
91        paint: {
92          'circle-color': '#B33D26',
93          'circle-radius': 8,
94          'circle-stroke-width': 1,
95          'circle-stroke-color': '#fff'
96        }
97      });
98      map.on('click', 'clusters', function(e) {
99        var clusterId, features;
100        features = map.queryRenderedFeatures(e.point, {
101          layers: ['clusters']
102        });
103        clusterId = features[0].properties.cluster_id;
104        map.getSource('globalimpact').getClusterExpansionZoom(clusterId, function(err, zoom) {
105          if (err) {
106            return;
107          }
108          map.easeTo({
109            center: features[0].geometry.coordinates,
110            zoom: zoom
111          });
112        });
113      });
114      map.on('mouseenter', 'clusters', function() {
115        map.getCanvas().style.cursor = 'pointer';
116      });
117      map.on('mouseleave', 'clusters', function() {
118        map.getCanvas().style.cursor = '';
119      });
120      map.on('click', 'unclustered-point', function(e) {
121        var coordinates, popupHtml, properties;
122        coordinates = e.features[0].geometry.coordinates.slice();
123        properties = e.features[0].properties;
124        popupHtml = '<div style="width:300px">';
125        popupHtml += '<h3>' + properties.tribe + '</h3>';
126        popupHtml += '<p>' + properties.text + '</p>';
127        popupHtml += '</div>';
128        while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
129          coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
130        }
131        (new mapboxgl.Popup).setLngLat(coordinates).setHTML(popupHtml).addTo(map);
132      });
133      map.on('mouseenter', 'unclustered-point', function() {
134        map.getCanvas().style.cursor = 'pointer';
135      });
136      map.on('mouseleave', 'unclustered-point', function() {
137        map.getCanvas().style.cursor = '';
138      });
139    });
140  }
141}

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.