PageSourceSearch

https://survivalbrasil.org/assets/controllers/numiko/what_we_do_map_controller-6130239f.js

js survivalbrasil.org collected 2026-09-25 18:53:42 UTC 5,105 bytes, 153 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2import supportsInteractionObserver from "utils/supports_interaction_observer"
3import mapboxgl from "mapbox-gl"
4
5// Connects to data-controller="numiko--what-we-do-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 map = new mapboxgl.Map({
38      container: 'svMap',
39      style: this.styleValue,
40      center: [0, 10],
41      projection: { name: 'winkelTripel' },
42      zoom: 1.2,
43      minZoom: 1.2,
44      scrollZoom: false,
45      dragRotate: false,
46      renderWorldCopies: false
47    });
48    let nav = new mapboxgl.NavigationControl();
49    map.addControl(nav, 'top-right');
50    map.on('load', function() {
51      map.addSource('globalimpact', {
52        type: 'geojson',
53        data: '/data/successes.' + locale + '.geojson',
54        cluster: true,
55        clusterMaxZoom: 14,
56        clusterRadius: 50
57      });
58      map.addLayer({
59        id: 'clusters',
60        type: 'circle',
61        source: 'globalimpact',
62        filter: ['has', 'point_count'],
63        paint: {
64          'circle-color': ['step', ['get', 'point_count'], '#B33D26', 100, '#B33D26', 750, '#B33D26'],
65          'circle-radius': ['step', ['get', 'point_count'], 20, 10, 30, 20, 40, 30, 60]
66        }
67      });
68      map.addLayer({
69        id: 'cluster-count',
70        type: 'symbol',
71        source: 'globalimpact',
72        filter: ['has', 'point_count'],
73        layout: {
74          'text-field': '{point_count_abbreviated}',
75          'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
76          'text-size': 14
77        },
78        paint: {
79          'text-color': '#ffffff'
80        }
81      });
82      map.addLayer({
83        id: 'unclustered-point',
84        type: 'circle',
85        source: 'globalimpact',
86        filter: ['!', ['has', 'point_count']],
87        paint: {
88          'circle-color': '#B33D26',
89          'circle-radius': 8,
90          'circle-stroke-width': 1,
91          'circle-stroke-color': '#fff'
92        }
93      });
94      map.on('click', 'clusters', function(e) {
95        var clusterId, features;
96        features = map.queryRenderedFeatures(e.point, {
97          layers: ['clusters']
98        });
99        clusterId = features[0].properties.cluster_id;
100        map.getSource('globalimpact').getClusterExpansionZoom(clusterId, function(err, zoom) {
101          if (err) {
102            return;
103          }
104          map.easeTo({
105            center: features[0].geometry.coordinates,
106            zoom: zoom + 1
107          });
108        });
109      });
110      map.on('mouseenter', 'clusters', function() {
111        map.getCanvas().style.cursor = 'pointer';
112      });
113      map.on('mouseleave', 'clusters', function() {
114        map.getCanvas().style.cursor = '';
115      });
116      map.on('click', 'unclustered-point', function(e) {
117        var coordinates, linktext, popupHtml, properties;
118        coordinates = e.features[0].geometry.coordinates.slice();
119        properties = e.features[0].properties;
120        popupHtml = '<h3>' + properties.name + '</h3>';
121        popupHtml += '<h4>' + properties.year || 'None' + '</h4>';
122        if (properties.multimedia) {
123          if (properties.multimedia === 'quotepic') {
124            popupHtml += '<img src="' + properties.source + '" />';
125          }
126          if (properties.multimedia === 'video') {
127            popupHtml += '<div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item" src="https://player.vimeo.com/video/' + properties.source + '?color=B33D26" allowfullscreen></iframe></div>';
128          }
129        }
130        if (properties.story) {
131          popupHtml += '<p>' + properties.story + '</p>';
132        }
133        if (properties.link) {
134          linktext = 'Find out more';
135          if (properties.linktext) {
136            linktext = properties.linktext;
137          }
138          popupHtml += '<a class="btn btn-primary" href="' + properties.link + '">' + linktext + '</a>';
139        }
140        while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
141          coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
142        }
143        (new mapboxgl.Popup).setLngLat(coordinates).setHTML(popupHtml).addTo(map);
144      });
145      map.on('mouseenter', 'unclustered-point', function() {
146        map.getCanvas().style.cursor = 'pointer';
147      });
148      map.on('mouseleave', 'unclustered-point', function() {
149        map.getCanvas().style.cursor = '';
150      });
151    });
152  }
153}

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.