PageSourceSearch

https://shsselect.com/assets/controllers/location_capture_controller-94326169.js

js shsselect.com collected 2026-09-24 18:24:54 UTC 6,488 bytes, 218 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static targets = ["latitude", "longitude", "accuracy", "gpsStatus", "gpsButton",
5                    "gpsForm", "mapContainer", "locationAccuracy", "fallbackOptions",
6                    "regionSelect", "districtSelect", "districtsData"]
7
8  connect() {
9    // Load districts data
10    if (this.hasDistrictsDataTarget) {
11      this.districtsData = JSON.parse(this.districtsDataTarget.textContent)
12    }
13    
14    this.map = null
15    this.marker = null
16    this.accuracyCircle = null
17  }
18  
19  disconnect() {
20    if (this.map) {
21      this.map.remove()
22    }
23    this.accuracyCircle = null
24    this.marker = null
25  }
26
27  requestGPS(event) {
28    event.preventDefault()
29    
30    if (!navigator.geolocation) {
31      this.showGPSStatus("GPS not supported by your browser", "error")
32      this.showFallbackOptions()
33      return
34    }
35
36    // Disable button and show loading
37    this.gpsButtonTarget.disabled = true
38    this.gpsButtonTarget.innerHTML = `
39      <svg class="animate-spin h-5 w-5 mr-3 inline" fill="none" viewBox="0 0 24 24">
40        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
41        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
42      </svg>
43      Getting your location...
44    `
45
46    navigator.geolocation.getCurrentPosition(
47      (position) => this.handleGPSSuccess(position),
48      (error) => this.handleGPSError(error),
49      {
50        enableHighAccuracy: true,
51        timeout: 15000,
52        maximumAge: 0
53      }
54    )
55  }
56
57  handleGPSSuccess(position) {
58    const lat = position.coords.latitude
59    const lon = position.coords.longitude
60    const accuracy = position.coords.accuracy
61
62    this.latitudeTarget.value = lat
63    this.longitudeTarget.value = lon
64    this.accuracyTarget.value = accuracy
65
66    // Hide button and status
67    this.gpsButtonTarget.classList.add("hidden")
68    this.gpsStatusTarget.classList.add("hidden")
69
70    // Show map with location
71    this.showMapPreview(lat, lon, accuracy)
72  }
73
74  handleGPSError(error) {
75    let message = "Could not get your location"
76    
77    switch(error.code) {
78      case error.PERMISSION_DENIED:
79        message = "Location permission denied."
80        break
81      case error.POSITION_UNAVAILABLE:
82        message = "Location information unavailable."
83        break
84      case error.TIMEOUT:
85        message = "Location request timed out."
86        break
87    }
88
89    this.showGPSStatus(message, "error")
90    
91    // Re-enable button
92    this.gpsButtonTarget.disabled = false
93    this.gpsButtonTarget.innerHTML = `
94      <svg class="w-6 h-6 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
95        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
96        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
97      </svg>
98      Share My Location
99    `
100    
101    // Show fallback options
102    this.showFallbackOptions()
103  }
104
105  showMapPreview(lat, lon, accuracy) {
106    this.mapContainerTarget.classList.remove("hidden")
107    this.locationAccuracyTarget.textContent = `Accuracy: ±${Math.round(accuracy)}m` 
108
109    // Check if Leaflet is available
110    if (typeof L === 'undefined') {
111      this.showGPSStatus("Map library not loaded. Please refresh the page.", "error")
112      return
113    }
114
115    // Initialize Leaflet map
116    if (!this.map) {
117      this.map = L.map('location-map').setView([lat, lon], 13)
118      
119      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
120        attribution: '© OpenStreetMap contributors',
121        maxZoom: 19
122      }).addTo(this.map)
123    } else {
124      this.map.setView([lat, lon], 13)
125    }
126
127    // Add marker
128    if (this.marker) {
129      this.marker.setLatLng([lat, lon])
130    } else {
131      this.marker = L.marker([lat, lon]).addTo(this.map)
132        .bindPopup('📍 Your Location')
133        .openPopup()
134    }
135
136    // Remove old accuracy circle if it exists
137    if (this.accuracyCircle) {
138      this.map.removeLayer(this.accuracyCircle)
139    }
140
141    // Add new accuracy circle
142    this.accuracyCircle = L.circle([lat, lon], {
143      radius: accuracy,
144      color: '#3b82f6',
145      fillColor: '#3b82f6',
146      fillOpacity: 0.1
147    }).addTo(this.map)
148  }
149
150  confirmLocation(event) {
151    event.preventDefault()
152    // Submit the form
153    this.gpsFormTarget.requestSubmit()
154  }
155
156  retryGPS(event) {
157    event.preventDefault()
158    
159    // Reset UI
160    this.mapContainerTarget.classList.add("hidden")
161    this.gpsButtonTarget.classList.remove("hidden")
162    this.gpsButtonTarget.disabled = false
163    this.fallbackOptionsTarget.classList.add("hidden")
164    
165    // Clear map and all resources
166    if (this.map) {
167      this.map.remove()
168      this.map = null
169      this.marker = null
170      this.accuracyCircle = null
171    }
172  }
173
174  showFallbackOptions() {
175    this.fallbackOptionsTarget.classList.remove("hidden")
176  }
177
178  showGPSStatus(message, type) {
179    this.gpsStatusTarget.textContent = message
180    this.gpsStatusTarget.classList.remove("hidden", "text-blue-600", "text-green-600", "text-red-600")
181    
182    if (type === "loading") {
183      this.gpsStatusTarget.classList.add("text-blue-600", "dark:text-blue-400")
184    } else if (type === "success") {
185      this.gpsStatusTarget.classList.add("text-green-600", "dark:text-green-400")
186    } else if (type === "error") {
187      this.gpsStatusTarget.classList.add("text-red-600", "dark:text-red-400")
188    }
189  }
190
191  updateDistricts(event) {
192    const regionId = parseInt(event.target.value)
193    const districtSelect = this.districtSelectTarget
194    
195    // Clear current options
196    districtSelect.innerHTML = '<option value="">Select a district...</option>'
197    
198    if (!regionId) {
199      districtSelect.disabled = true
200      return
201    }
202
203    // Find the selected region
204    const region = this.districtsData.find(r => r.id === regionId)
205    
206    if (region && region.districts) {
207      // Add district options
208      region.districts.forEach(district => {
209        const option = document.createElement('option')
210        option.value = district.id
211        option.textContent = district.name
212        districtSelect.appendChild(option)
213      })
214      
215      districtSelect.disabled = false
216    }
217  }
218}

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.