PageSourceSearch

https://snow-lemur-379076.hostingersite.com/wp-content/plugins/eca…/assets/admin/mptbm_map.js?ver=1788859684

js snow-lemur-379076.hostingersite.com collected 2026-10-03 04:55:48 UTC 51,886 bytes, 1,389 lines download raw bytes

1var mapOptions;
2var map;
3
4var coordinates = [];
5let new_coordinates = [];
6let lastElemen;
7var geoLocationOne;
8var formattedAddress;
9function notifyMPTBMOperationAreaField(inputId) {
10    var input = document.getElementById(inputId);
11    if (input) {
12        input.dispatchEvent(new Event('change', { bubbles: true }));
13    }
14}
15function InitMapOne(geoLocationOne, locationAddress) {
16    var mapCanvas1 = document.getElementById('mptbm-map-canvas-one');
17    if (mapCanvas1) {
18        var slotFormattedAddress = locationAddress || (document.getElementById('mptbm-starting-location-one') ? document.getElementById('mptbm-starting-location-one').value : '');
19        if(geoLocationOne===undefined){
20            geoLocationOne = new google.maps.LatLng(23.8103, 90.4125);
21        }
22        
23        mapOptions = {
24            zoom: 10,
25            center: geoLocationOne,
26            mapTypeId: google.maps.MapTypeId.RoadMap
27        };
28        map = new google.maps.Map(mapCanvas1, mapOptions);
29
30        var all_overlays = [];
31        var selectedShape;
32        var drawingManager = new google.maps.drawing.DrawingManager({
33            drawingControlOptions: {
34                position: google.maps.ControlPosition.TOP_CENTER,
35                drawingModes: [
36                    google.maps.drawing.OverlayType.POLYGON,
37                ]
38            },
39            circleOptions: {
40                fillColor: '#ffff00',
41                fillOpacity: 0.2,
42                strokeWeight: 3,
43                clickable: false,
44                editable: true,
45                zIndex: 1
46            },
47            polygonOptions: {
48                clickable: true,
49                draggable: false,
50                editable: true,
51                fillColor: '#635bff',
52                fillOpacity: 0.5
53            },
54            rectangleOptions: {
55                clickable: true,
56                draggable: true,
57                editable: true,
58                fillColor: '#ffff00',
59                fillOpacity: 0.5
60            }
61        });
62
63        function clearSelection() {
64            if (selectedShape) {
65                selectedShape.setEditable(false);
66                selectedShape = null;
67            }
68        }
69
70        function stopDrawing() {
71            drawingManager.setMap(null);
72        }
73
74        function setSelection(shape) {
75            clearSelection();
76            stopDrawing();
77            selectedShape = shape;
78            shape.setEditable(true);
79        }
80
81        function deleteSelectedShape() {
82            if (selectedShape) {
83                selectedShape.setMap(null);
84                drawingManager.setMap(map);
85                coordinates.splice(0, coordinates.length);
86                document.getElementById('mptbm-coordinates-one').value = '';
87                notifyMPTBMOperationAreaField('mptbm-coordinates-one');
88            }
89        }
90
91        function CenterControl(controlDiv, map) {
92            var controlUI = document.createElement('div');
93            controlUI.style.backgroundColor = '#fff';
94            controlUI.style.border = '2px solid #fff';
95            controlUI.style.borderRadius = '3px';
96            controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
97            controlUI.style.cursor = 'pointer';
98            controlUI.style.marginBottom = '22px';
99            controlUI.style.textAlign = 'center';
100            controlUI.title = 'Select to delete the shape';
101            controlDiv.appendChild(controlUI);
102
103            var controlText = document.createElement('div');
104            controlText.style.color = 'rgb(25,25,25)';
105            controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
106            controlText.style.fontSize = '16px';
107            controlText.style.lineHeight = '38px';
108            controlText.style.paddingLeft = '5px';
109            controlText.style.paddingRight = '5px';
110            controlText.innerHTML = 'Delete Selected Area';
111            controlUI.appendChild(controlText);
112
113            controlUI.addEventListener('click', function () {
114                deleteSelectedShape();
115            });
116        }
117
118        drawingManager.setMap(map);
119
120        var getPolygonCoords = function (newShape) {
121            coordinates.splice(0, coordinates.length);
122            var len = newShape.getPath().getLength();
123            for (var i = 0; i < len; i++) {
124                coordinates.push(newShape.getPath().getAt(i).toUrlValue(6));
125            }
126            document.getElementById('mptbm-starting-location-one-hidden').value = slotFormattedAddress;
127            document.getElementById('mptbm-coordinates-one').value = coordinates;
128            notifyMPTBMOperationAreaField('mptbm-starting-location-one-hidden');
129            notifyMPTBMOperationAreaField('mptbm-coordinates-one');
130        };
131
132        google.maps.event.addListener(drawingManager, 'polygoncomplete', function (event) {
133            getPolygonCoords(event);
134            google.maps.event.addListener(event, "dragend", getPolygonCoords(event));
135            google.maps.event.addListener(event.getPath(), 'insert_at', function () {
136                getPolygonCoords(event);
137            });
138            google.maps.event.addListener(event.getPath(), 'set_at', function () {
139                getPolygonCoords(event);
140            });
141        });
142
143        google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
144            all_overlays.push(event);
145            if (event.type !== google.maps.drawing.OverlayType.MARKER) {
146                drawingManager.setDrawingMode(null);
147                var newShape = event.overlay;
148                newShape.type = event.type;
149                google.maps.event.addListener(newShape, 'click', function () {
150                    setSelection(newShape);
151                });
152                setSelection(newShape);
153            }
154        });
155
156        var centerControlDiv = document.createElement('div');
157        var centerControl = new CenterControl(centerControlDiv, map);
158        centerControlDiv.index = 1;
159        map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(centerControlDiv);
160    }
161}
162function InitMapTwo(geoLocationOne, locationAddress) {
163    var mapCanvas1 = document.getElementById('mptbm-map-canvas-two');
164    if (mapCanvas1) {
165        var slotFormattedAddress = locationAddress || (document.getElementById('mptbm-starting-location-two') ? document.getElementById('mptbm-starting-location-two').value : '');
166        if(geoLocationOne===undefined){
167            geoLocationOne = new google.maps.LatLng(23.8103, 90.4125);
168        }
169        
170        mapOptions = {
171            zoom: 10,
172            center: geoLocationOne,
173            mapTypeId: google.maps.MapTypeId.RoadMap
174        };
175        map = new google.maps.Map(mapCanvas1, mapOptions);
176
177        var all_overlays = [];
178        var selectedShape;
179        var drawingManager = new google.maps.drawing.DrawingManager({
180            drawingControlOptions: {
181                position: google.maps.ControlPosition.TOP_CENTER,
182                drawingModes: [
183                    google.maps.drawing.OverlayType.POLYGON,
184                ]
185            },
186            circleOptions: {
187                fillColor: '#ffff00',
188                fillOpacity: 0.2,
189                strokeWeight: 3,
190                clickable: false,
191                editable: true,
192                zIndex: 1
193            },
194            polygonOptions: {
195                clickable: true,
196                draggable: false,
197                editable: true,
198                fillColor: '#635bff',
199                fillOpacity: 0.5
200            },
201            rectangleOptions: {
202                clickable: true,
203                draggable: true,
204                editable: true,
205                fillColor: '#ffff00',
206                fillOpacity: 0.5
207            }
208        });
209
210        function clearSelection() {
211            if (selectedShape) {
212                selectedShape.setEditable(false);
213                selectedShape = null;
214            }
215        }
216
217        function stopDrawing() {
218            drawingManager.setMap(null);
219        }
220
221        function setSelection(shape) {
222            clearSelection();
223            stopDrawing();
224            selectedShape = shape;
225            shape.setEditable(true);
226        }
227
228        function deleteSelectedShape() {
229            if (selectedShape) {
230                selectedShape.setMap(null);
231                drawingManager.setMap(map);
232                coordinates.splice(0, coordinates.length);
233                document.getElementById('mptbm-coordinates-two').value = '';
234                notifyMPTBMOperationAreaField('mptbm-coordinates-two');
235            }
236        }
237
238        function CenterControl(controlDiv, map) {
239            var controlUI = document.createElement('div');
240            controlUI.style.backgroundColor = '#fff';
241            controlUI.style.border = '2px solid #fff';
242            controlUI.style.borderRadius = '3px';
243            controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
244            controlUI.style.cursor = 'pointer';
245            controlUI.style.marginBottom = '22px';
246            controlUI.style.textAlign = 'center';
247            controlUI.title = 'Select to delete the shape';
248            controlDiv.appendChild(controlUI);
249
250            var controlText = document.createElement('div');
251            controlText.style.color = 'rgb(25,25,25)';
252            controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
253            controlText.style.fontSize = '16px';
254            controlText.style.lineHeight = '38px';
255            controlText.style.paddingLeft = '5px';
256            controlText.style.paddingRight = '5px';
257            controlText.innerHTML = 'Delete Selected Area';
258            controlUI.appendChild(controlText);
259
260            controlUI.addEventListener('click', function () {
261                deleteSelectedShape();
262            });
263        }
264
265        drawingManager.setMap(map);
266
267        var getPolygonCoords = function (newShape) {
268            coordinates.splice(0, coordinates.length);
269            var len = newShape.getPath().getLength();
270            for (var i = 0; i < len; i++) {
271                coordinates.push(newShape.getPath().getAt(i).toUrlValue(6));
272            }
273            document.getElementById('mptbm-starting-location-two-hidden').value = slotFormattedAddress;
274            document.getElementById('mptbm-coordinates-two').value = coordinates;
275            notifyMPTBMOperationAreaField('mptbm-starting-location-two-hidden');
276            notifyMPTBMOperationAreaField('mptbm-coordinates-two');
277        };
278
279        google.maps.event.addListener(drawingManager, 'polygoncomplete', function (event) {
280            getPolygonCoords(event);
281            google.maps.event.addListener(event, "dragend", getPolygonCoords(event));
282            google.maps.event.addListener(event.getPath(), 'insert_at', function () {
283                getPolygonCoords(event);
284            });
285            google.maps.event.addListener(event.getPath(), 'set_at', function () {
286                getPolygonCoords(event);
287            });
288        });
289
290        google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
291            all_overlays.push(event);
292            if (event.type !== google.maps.drawing.OverlayType.MARKER) {
293                drawingManager.setDrawingMode(null);
294                var newShape = event.overlay;
295                newShape.type = event.type;
296                google.maps.event.addListener(newShape, 'click', function () {
297                    setSelection(newShape);
298                });
299                setSelection(newShape);
300            }
301        });
302
303        var centerControlDiv = document.createElement('div');
304        var centerControl = new CenterControl(centerControlDiv, map);
305        centerControlDiv.index = 1;
306        map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(centerControlDiv);
307    }
308}
309function InitMapFixed(geoLocationOne, locationAddress) {
310    var mapCanvas3 = document.getElementById('mptbm-map-canvas-three');
311    if (mapCanvas3) {
312        var slotFormattedAddress = locationAddress || (document.getElementById('mptbm-starting-location-three') ? document.getElementById('mptbm-starting-location-three').value : '');
313        if(geoLocationOne===undefined){
314            geoLocationOne = new google.maps.LatLng(23.8103, 90.4125);
315        }
316        
317        mapOptions = {
318            zoom: 10,
319            center: geoLocationOne,
320            mapTypeId: google.maps.MapTypeId.RoadMap
321        };
322        map = new google.maps.Map(mapCanvas3, mapOptions);
323
324        var all_overlays = [];
325        var selectedShape;
326        var drawingManager = new google.maps.drawing.DrawingManager({
327            drawingControlOptions: {
328                position: google.maps.ControlPosition.TOP_CENTER,
329                drawingModes: [
330                    google.maps.drawing.OverlayType.POLYGON,
331                ]
332            },
333            circleOptions: {
334                fillColor: '#ffff00',
335                fillOpacity: 0.2,
336                strokeWeight: 3,
337                clickable: false,
338                editable: true,
339                zIndex: 1
340            },
341            polygonOptions: {
342                clickable: true,
343                draggable: false,
344                editable: true,
345                fillColor: '#635bff',
346                fillOpacity: 0.5
347            },
348            rectangleOptions: {
349                clickable: true,
350                draggable: true,
351                editable: true,
352                fillColor: '#ffff00',
353                fillOpacity: 0.5
354            }
355        });
356
357        function clearSelection() {
358            if (selectedShape) {
359                selectedShape.setEditable(false);
360                selectedShape = null;
361            }
362        }
363
364        function stopDrawing() {
365            drawingManager.setMap(null);
366        }
367
368        function setSelection(shape) {
369            clearSelection();
370            stopDrawing();
371            selectedShape = shape;
372            shape.setEditable(true);
373        }
374
375        function deleteSelectedShape() {
376            if (selectedShape) {
377                selectedShape.setMap(null);
378                drawingManager.setMap(map);
379                coordinates.splice(0, coordinates.length);
380                document.getElementById('mptbm-coordinates-three').value = '';
381                notifyMPTBMOperationAreaField('mptbm-coordinates-three');
382            }
383        }
384
385        function CenterControl(controlDiv, map) {
386            var controlUI = document.createElement('div');
387            controlUI.style.backgroundColor = '#fff';
388            controlUI.style.border = '2px solid #fff';
389            controlUI.style.borderRadius = '3px';
390            controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
391            controlUI.style.cursor = 'pointer';
392            controlUI.style.marginBottom = '22px';
393            controlUI.style.textAlign = 'center';
394            controlUI.title = 'Select to delete the shape';
395            controlDiv.appendChild(controlUI);
396
397            var controlText = document.createElement('div');
398            controlText.style.color = 'rgb(25,25,25)';
399            controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
400            controlText.style.fontSize = '16px';
401            controlText.style.lineHeight = '38px';
402            controlText.style.paddingLeft = '5px';
403            controlText.style.paddingRight = '5px';
404            controlText.innerHTML = 'Delete Selected Area';
405            controlUI.appendChild(controlText);
406
407            controlUI.addEventListener('click', function () {
408                deleteSelectedShape();
409            });
410        }
411
412        drawingManager.setMap(map);
413
414        var getPolygonCoords = function (newShape) {
415            coordinates.splice(0, coordinates.length);
416            var len = newShape.getPath().getLength();
417            for (var i = 0; i < len; i++) {
418                coordinates.push(newShape.getPath().getAt(i).toUrlValue(6));
419            }
420            document.getElementById('mptbm-starting-location-three-hidden').value = slotFormattedAddress;
421            document.getElementById('mptbm-coordinates-three').value = coordinates;
422            notifyMPTBMOperationAreaField('mptbm-starting-location-three-hidden');
423            notifyMPTBMOperationAreaField('mptbm-coordinates-three');
424        };
425
426        google.maps.event.addListener(drawingManager, 'polygoncomplete', function (event) {
427            getPolygonCoords(event);
428            google.maps.event.addListener(event, "dragend", getPolygonCoords(event));
429            google.maps.event.addListener(event.getPath(), 'insert_at', function () {
430                getPolygonCoords(event);
431            });
432            google.maps.event.addListener(event.getPath(), 'set_at', function () {
433                getPolygonCoords(event);
434            });
435        });
436
437        google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) {
438            all_overlays.push(event);
439            if (event.type !== google.maps.drawing.OverlayType.MARKER) {
440                drawingManager.setDrawingMode(null);
441                var newShape = event.overlay;
442                newShape.type = event.type;
443                google.maps.event.addListener(newShape, 'click', function () {
444                    setSelection(newShape);
445                });
446                setSelection(newShape);
447            }
448        });
449
450        var centerControlDiv = document.createElement('div');
451        var centerControl = new CenterControl(centerControlDiv, map);
452        centerControlDiv.index = 1;
453        map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(centerControlDiv);
454    }
455}
456
457// Map initialization is now handled in the PHP template
458// based on whether coordinates are saved or not.
459// Do not auto-initialize here to avoid conflicts.
460
461
462
463function iniSavedtMap(coordinates,mapCanvasId,mapAppendId) {
464
465    var all_overlays = [];
466    var selectedShape;
467    drawingManager = new google.maps.drawing.DrawingManager({
468        drawingControlOptions: {
469            position: google.maps.ControlPosition.TOP_CENTER,
470            drawingModes: [
471                google.maps.drawing.OverlayType.POLYGON,
472            ]
473        },
474        polygonOptions: {
475            clickable: true,
476            draggable: false,
477            editable: true,
478            fillColor: '#635bff',
479            fillOpacity: 0.5
480        }
481    });
482
483    google.maps.event.addListener(drawingManager, 'polygoncomplete', function(event) {
484        getPolygonCoords(event);
485        google.maps.event.addListener(event, "dragend", getPolygonCoords(event));
486        google.maps.event.addListener(event.getPath(), 'insert_at', function() {
487            getPolygonCoords(event);
488        });
489        google.maps.event.addListener(event.getPath(), 'set_at', function() {
490            getPolygonCoords(event);
491        });
492
493    });
494    google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
495        drawingManager.setOptions({
496            drawingControl: false
497        });
498        all_overlays.push(event);
499        if (event.type !== google.maps.drawing.OverlayType.MARKER) {
500            drawingManager.setDrawingMode(null);
501            var newShape = event.overlay;
502            newShape.type = event.type;
503            google.maps.event.addListener(newShape, 'click', function() {
504                setSelection(newShape);
505            });
506            setSelection(newShape);
507        }
508    });
509
510    function clearSelection() {
511        if (selectedShape) {
512            selectedShape.setEditable(false);
513            selectedShape = null;
514        }
515    }
516
517    function setSelection(shape) {
518        clearSelection();
519        stopDrawing();
520        selectedShape = shape;
521        shape.setEditable(true);
522    }
523    var getPolygonCoords = function(newShape) {
524        coordinates.splice(0, coordinates.length);
525        var len = newShape.getPath().getLength();
526        for (var i = 0; i < len; i++) {
527            coordinates.push(newShape.getPath().getAt(i).toUrlValue(6));
528        }
529        if(mapAppendId != null){
530            document.getElementById(mapAppendId).value = coordinates;
531            notifyMPTBMOperationAreaField(mapAppendId);
532        }
533    };
534
535    // Create map centered at the first coordinate
536    var map = new google.maps.Map(document.getElementById(mapCanvasId), {
537        center: {
538            lat: parseFloat(coordinates[0]),
539            lng: parseFloat(coordinates[1])
540        },
541        zoom: 10 // Set zoom level to 12
542    });
543
544    // Create an array to store LatLng objects
545    var path = [];
546    for (var i = 0; i < coordinates.length; i += 2) {
547        var latLng = new google.maps.LatLng(parseFloat(coordinates[i]), parseFloat(coordinates[i + 1])
547);
548        path.push(latLng);
549    }
550
551    // Construct the polygon
552    var polygon = new google.maps.Polygon({
553        paths: path,
554        strokeColor: "#000000", // Change to black
555        strokeOpacity: 0.8,
556        strokeWeight: 4, // Increase the thickness
557        fillColor: "#635bff",
558        fillOpacity: 0.5, // Adjust fill opacity
559        editable: false // Make the polygon editable
560    });
561
562    // Set polygon on the map
563    polygon.setMap(map);
564    google.maps.event.addListener(polygon, 'click', function() {
565        setSelection(polygon);
566    });
567    google.maps.event.addListener(polygon.getPath(), 'insert_at', function() {
568        getPolygonCoords(polygon);
569    });
570    google.maps.event.addListener(polygon.getPath(), 'set_at', function() {
571        getPolygonCoords(polygon);
572    });
573
574    // Function to calculate the center of the polygon
575    function calculateCenter() {
576        var bounds = new google.maps.LatLngBounds();
577        path.forEach(function(latLng) {
578            bounds.extend(latLng);
579        });
580        return bounds.getCenter();
581    }
582
583    // Center map on the calculated center of the polygon
584    map.setCenter(calculateCenter());
585
586    // Delete selected shape function
587    function deleteSelectedShape() {
588
589        if (selectedShape != undefined) {
590            selectedShape.setMap(null);
591            drawingManager.setMap(map);
592            coordinates.splice(0, coordinates.length);
593        }
594        drawingManager.setOptions({
595            drawingControl: true
596        });
597        if (polygon) {
598            polygon.setMap(null);
599            drawingManager.setMap(map);
600            coordinates.splice(0, coordinates.length);
601        }
602        if (mapAppendId) {
603            document.getElementById(mapAppendId).value = '';
604            notifyMPTBMOperationAreaField(mapAppendId);
605        }
606
607    }
608    function stopDrawing() {
609        drawingManager.setMap(null);
610    }
611    // Add delete button control
612    var deleteControlDiv = document.createElement('div');
613    var deleteControl = new CenterControl(deleteControlDiv, map);
614
615    if(mapAppendId != null){
616        map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(deleteControlDiv);
617    }
618
619    function CenterControl(controlDiv, map) {
620        // Create the button container
621        var controlUI = document.createElement('div');
622        controlUI.style.backgroundColor = '#fff';
623        controlUI.style.border = '2px solid #fff';
624        controlUI.style.borderRadius = '3px';
625        controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
626        controlUI.style.cursor = 'pointer';
627        controlUI.style.textAlign = 'center';
628        controlUI.title = 'Select to delete the shape';
629        controlDiv.appendChild(controlUI);
630
631        // Create the text inside the button
632        var controlText = document.createElement('div');
633        controlText.style.color = 'rgb(25,25,25)';
634        controlText.style.fontFamily = 'Roboto,Arial,sans-serif';
635        controlText.style.fontSize = '16px';
636        controlText.style.lineHeight = '38px';
637        controlText.style.paddingLeft = '5px';
638        controlText.style.paddingRight = '5px';
639        controlText.innerHTML = 'Delete Selected Area';
640        controlUI.appendChild(controlText);
641
642        // Add click event listener to the button
643        controlUI.addEventListener('click', function() {
644            deleteSelectedShape();
645        });
646
647        // Add some margin
648        controlDiv.style.marginBottom = '10px'; // Adjust margin as needed
649
650        // Center the button
651        controlDiv.style.padding = '5px';
652        controlDiv.style.width = 'fit-content';
653    }
654}
655
656(function ($) {
657
658    $(document).ready(function () {
659        
660        // Initialize Google Places autocomplete instances only once
661        function initializeAutocomplete(inputId, mapFunction) {
662            var input = document.getElementById(inputId);
663            if (!input || input.hasAttribute('data-autocomplete-initialized')) {
664                return;
665            }
666            
667            var autocomplete = new google.maps.places.Autocomplete(input, { types: ['geocode'] });
668            
669            autocomplete.addListener('place_changed', function() {
670                var place = autocomplete.getPlace();
671                formattedAddress = place.formatted_address;
672                if (place.geometry) {
673                    var location = place.geometry.location;
674                    var slot = inputId.split('-').pop();
675                    var coordinatesInput = document.getElementById('mptbm-coordinates-' + slot);
676                    var locationInput = document.getElementById('mptbm-starting-location-' + slot + '-hidden');
677                    if (coordinatesInput) {
678                        coordinatesInput.value = '';
679                        notifyMPTBMOperationAreaField(coordinatesInput.id);
680                    }
681                    if (locationInput) {
682                        locationInput.value = formattedAddress || input.value;
683                        notifyMPTBMOperationAreaField(locationInput.id);
684                    }
685                    mapFunction(location, formattedAddress);
686                }
687            });
688            
689            // Mark as initialized to prevent duplicate initialization
690            input.setAttribute('data-autocomplete-initialized', 'true');
691        }
692        
693        // Initialize autocomplete for all three location inputs
694        // Only initialize Google Maps autocomplete if Google Maps API is loaded
695        // OpenStreetMap autocomplete is handled in the OSM functions
696        if (typeof google !== 'undefined' && typeof google.maps !== 'undefined') {
697            initializeAutocomplete('mptbm-starting-location-one', InitMapOne);
698            initializeAutocomplete('mptbm-starting-location-two', InitMapTwo);
699            initializeAutocomplete('mptbm-starting-location-three', InitMapFixed);
700        }
701        
702    });
703    
704})(jQuery);
705
706// ==========================================
707// OpenStreetMap Functions for Operation Areas
708// ==========================================
709
710// Global OSM variables
711var osmMapOne, osmMapTwo, osmMapFixed;
712var osmDrawLayerOne, osmDrawLayerTwo, osmDrawLayerFixed;
713var osmDrawControlOne, osmDrawControlTwo, osmDrawControlFixed;
714
715// Keep saved and newly-created modal maps in the same public slots. The
716// Operation Area builder uses these references for its accessible Draw/Edit/
717// Fit/Clear buttons, while the original Leaflet.draw toolbar remains intact.
718function registerOSMOperationAreaMap(mapCanvasId, mapInstance, drawLayer, drawControl) {
719    var slot = String(mapCanvasId || '').split('-').pop();
720    var suffix = slot ? slot.charAt(0).toUpperCase() + slot.slice(1) : '';
721    if (!suffix) return;
722    window['osmMap' + suffix] = mapInstance;
723    window['osmDrawLayer' + suffix] = drawLayer;
724    window['osmDrawControl' + suffix] = drawControl;
725    if (slot === 'one') {
726        osmMapOne = mapInstance;
727        osmDrawLayerOne = drawLayer;
728        osmDrawControlOne = drawControl;
729    } else if (slot === 'two') {
730        osmMapTwo = mapInstance;
731        osmDrawLayerTwo = drawLayer;
732        osmDrawControlTwo = drawControl;
733    } else if (slot === 'three') {
734        osmMapFixed = mapInstance;
735        osmDrawLayerFixed = drawLayer;
736        osmDrawControlFixed = drawControl;
737    }
738    setupOSMFirstPointGuide(mapCanvasId, mapInstance);
739    setupOSMControlTooltips(mapCanvasId);
740}
741
742// Browser title bubbles are inconsistent inside Leaflet's stacked map panes.
743// Add visible, accessible labels to the native polygon/edit/delete controls.
744function setupOSMControlTooltips(mapCanvasId) {
745    var mapCanvas = document.getElementById(mapCanvasId);
746    if (!mapCanvas) return;
747
748    window.setTimeout(function() {
749        var translations = window.mptbmOperationAreas || {};
750        var controls = [
751            [ '.leaflet-draw-draw-polygon', translations.drawBoundary || 'Draw a new boundary' ],
752            [ '.leaflet-draw-edit-edit', translations.editBoundary || 'Edit boundary points' ],
753            [ '.leaflet-draw-edit-remove', translations.deleteBoundary || 'Delete the boundary' ]
754        ];
755
756        controls.forEach(function(control) {
757            var button = mapCanvas.querySelector(control[0]);
758            if (!button) return;
759            button.setAttribute('data-mptbm-tooltip', control[1]);
760            button.setAttribute('aria-label', control[1]);
761            button.setAttribute('title', control[1]);
762        });
763    }, 0);
764}
765
766// Leaflet.draw renders every vertex with the same small square. Highlight the
767// first vertex during polygon creation so users always know where to click to
768// close the boundary. This is presentation-only; Leaflet keeps handling the
769// click and polygon completion exactly as before.
770function setupOSMFirstPointGuide(mapCanvasId, mapInstance) {
771    var mapCanvas = document.getElementById(mapCanvasId);
772    if (!mapCanvas || !mapInstance || mapInstance._mptbmFirstPointGuide) return;
773
774    var mapShell = mapCanvas.closest('.mptbm-operation-area-map-shell');
775    var finishLabel = window.mptbmOperationAreas && window.mptbmOperationAreas.finishAtStart
776        ? window.mptbmOperationAreas.finishAtStart
777        : 'START — click to finish';
778
779    function clearFirstPoint() {
780        mapCanvas.querySelectorAll('.mptbm-first-draw-point').forEach(function(marker) {
781            marker.classList.remove('mptbm-first-draw-point');
782            marker.classList.remove('is-label-left');
783            marker.removeAttribute('data-finish-label');
784        });
785        if (mapShell) mapShell.classList.remove('is-drawing-boundary');
786    }
787
788    function markFirstPoint(event) {
789        window.setTimeout(function() {
790            var markers = event && event.layers && typeof event.layers.getLayers === 'function'
791                ? event.layers.getLayers()
792                : [];
793            var firstMarker = markers.length && markers[0]._icon
794                ? markers[0]._icon
795                : mapCanvas.querySelector('.leaflet-marker-pane .leaflet-editing-icon');
796
797            mapCanvas.querySelectorAll('.mptbm-first-draw-point').forEach(function(marker) {
798                if (marker !== firstMarker) marker.classList.remove('mptbm-first-draw-point');
799            });
800            if (firstMarker) {
801                firstMarker.classList.add('mptbm-first-draw-point');
802                firstMarker.setAttribute('data-finish-label', finishLabel);
803                var markerRect = firstMarker.getBoundingClientRect();
804                var canvasRect = mapCanvas.getBoundingClientRect();
805                firstMarker.classList.toggle('is-label-left', markerRect.left > canvasRect.left + (canvasRect.width * 0.68));
806            }
807        }, 0);
808    }
809
810    mapInstance.on(L.Draw.Event.DRAWSTART, function() {
811        clearFirstPoint();
812        if (mapShell) mapShell.classList.add('is-drawing-boundary');
813    });
814    mapInstance.on(L.Draw.Event.DRAWVERTEX, markFirstPoint);
815    mapInstance.on(L.Draw.Event.DRAWSTOP, clearFirstPoint);
816    mapInstance.on(L.Draw.Event.CREATED, clearFirstPoint);
817    mapInstance._mptbmFirstPointGuide = true;
818}
819
820// Let the guided Operation Area UI react immediately when Leaflet changes a
821// hidden field. Native DOM assignments do not fire jQuery change handlers.
822function notifyOSMOperationAreaField(inputId) {
823    notifyMPTBMOperationAreaField(inputId);
824}
825
826// Initialize OSM Map One (Intercity - Location 1)
827function InitOSMMapOne(geoLocation) {
828    var mapCanvas1 = document.getElementById('mptbm-map-canvas-one');
829    if (!mapCanvas1) return;
830    
831    // Check if already initialized and clean up
832    if (osmMapOne) {
833        try {
834            osmMapOne.remove();
835        } catch (e) {
836            console.log('[OSM] Error removing map:', e);
837        }
838        osmMapOne = null;
839    }
840    
841    // Clear Leaflet's internal reference on the container
842    mapCanvas1._leaflet_id = null;
843    mapCanvas1.innerHTML = '';
844    
845    // Default location: Dhaka
846    var defaultLat = geoLocation ? geoLocation.lat : 23.8103;
847    var defaultLng = geoLocation ? geoLocation.lng : 90.4125;
848    
849    // Initialize map
850    osmMapOne = L.map('mptbm-map-canvas-one').setView([defaultLat, defaultLng], 10);
851    
852    // Add OpenStreetMap tiles
853    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
854        attribution: '© OpenStreetMap contributors'
855    }).addTo(osmMapOne);
856    
857    // Feature group to store drawn items
858    osmDrawLayerOne = new L.FeatureGroup();
859    osmMapOne.addLayer(osmDrawLayerOne);
860    
861    // Initialize draw control
862    osmDrawControlOne = new L.Control.Draw({
863        position: 'topright',
864        draw: {
865            polygon: {
866                allowIntersection: false,
867                drawError: {
868                    color: '#e1e100',
869                    message: '<strong>Error:</strong> Shape edges cannot cross!'
870                },
871                shapeOptions: {
872                    color: '#635bff',
873                    fillOpacity: 0.5
874                }
875            },
876            polyline: false,
877            circle: false,
878            rectangle: false,
879            marker: false,
880            circlemarker: false
881        },
882        edit: {
883            featureGroup: osmDrawLayerOne,
884            remove: true
885        }
886    });
887    osmMapOne.addControl(osmDrawControlOne);
888    registerOSMOperationAreaMap('mptbm-map-canvas-one', osmMapOne, osmDrawLayerOne, osmDrawControlOne);
889    
890    // Handle polygon creation
891    osmMapOne.on(L.Draw.Event.CREATED, function (e) {
892        osmDrawLayerOne.clearLayers();
893        var layer = e.layer;
894        osmDrawLayerOne.addLayer(layer);
895        saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-one');
896    });
897    
898    // Handle polygon edit
899    osmMapOne.on(L.Draw.Event.EDITED, function (e) {
900        var layers = e.layers;
901        layers.eachLayer(function (layer) {
902            saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-one');
903        });
904    });
905    
906    // Handle polygon delete
907    osmMapOne.on(L.Draw.Event.DELETED, function (e) {
908        document.getElementById('mptbm-coordinates-one').value = '';
909        notifyOSMOperationAreaField('mptbm-coordinates-one');
910    });
911    
912    // Setup autocomplete for location search
913    setupOSMLocationSearch('mptbm-starting-location-one', osmMapOne, function(lat, lng, displayName) {
914        osmMapOne.setView([lat, lng], 13);
915        osmDrawLayerOne.clearLayers();
916        document.getElementById('mptbm-starting-location-one-hidden').value = displayName;
917        document.getElementById('mptbm-coordinates-one').value = '';
918        notifyOSMOperationAreaField('mptbm-starting-location-one-hidden');
919        notifyOSMOperationAreaField('mptbm-coordinates-one');
920    });
921    
922    // Force Leaflet to recalculate map size (fixes partial rendering)
923    // Longer delay to ensure container is fully visible
924    setTimeout(function() {
925        osmMapOne.invalidateSize();
926    }, 500);
927}
928
929// Initialize OSM Map Two (Intercity - Location 2)
930function InitOSMMapTwo(geoLocation) {
931    var mapCanvas2 = document.getElementById('mptbm-map-canvas-two');
932    if (!mapCanvas2) return;
933    
934    // Check if already initialized and clean up
935    if (osmMapTwo) {
936        try {
937            osmMapTwo.remove();
938        } catch (e) {
939            console.log('[OSM] Error removing map:', e);
940        }
941        osmMapTwo = null;
942    }
943    
944    // Clear Leaflet's internal reference on the container
945    mapCanvas2._leaflet_id = null;
946    mapCanvas2.innerHTML = '';
947    
948    var defaultLat = geoLocation ? geoLocation.lat : 23.8103;
949    var defaultLng = geoLocation ? geoLocation.lng : 90.4125;
950    
951    osmMapTwo = L.map('mptbm-map-canvas-two').setView([defaultLat, defaultLng], 10);
952    
953    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
954        attribution: '© OpenStreetMap contributors'
955    }).addTo(osmMapTwo);
956    
957    osmDrawLayerTwo = new L.FeatureGroup();
958    osmMapTwo.addLayer(osmDrawLayerTwo);
959    
960    osmDrawControlTwo = new L.Control.Draw({
961        position: 'topright',
962        draw: {
963            polygon: {
964                allowIntersection: false,
965                drawError: {
966                    color: '#e1e100',
967                    message: '<strong>Error:</strong> Shape edges cannot cross!'
968                },
969                shapeOptions: {
970                    color: '#635bff',
971                    fillOpacity: 0.5
972                }
973            },
974            polyline: false,
975            circle: false,
976            rectangle: false,
977            marker: false,
978            circlemarker: false
979        },
980        edit: {
981            featureGroup: osmDrawLayerTwo,
982            remove: true
983        }
984    });
985    osmMapTwo.addControl(osmDrawControlTwo);
986    registerOSMOperationAreaMap('mptbm-map-canvas-two', osmMapTwo, osmDrawLayerTwo, osmDrawControlTwo);
987    
988    osmMapTwo.on(L.Draw.Event.CREATED, function (e) {
989        osmDrawLayerTwo.clearLayers();
990        var layer = e.layer;
991        osmDrawLayerTwo.addLayer(layer);
992        saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-two');
993    });
994    
995    osmMapTwo.on(L.Draw.Event.EDITED, function (e) {
996        var layers = e.layers;
997        layers.eachLayer(function (layer) {
998            saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-two');
999        });
1000    });
1001    
1002    osmMapTwo.on(L.Draw.Event.DELETED, function (e) {
1003        document.getElementById('mptbm-coordinates-two').value = '';
1004        notifyOSMOperationAreaField('mptbm-coordinates-two');
1005    });
1006    
1007    setupOSMLocationSearch('mptbm-starting-location-two', osmMapTwo, function(lat, lng, displayName) {
1008        osmMapTwo.setView([lat, lng], 13);
1009        osmDrawLayerTwo.clearLayers();
1010        document.getElementById('mptbm-starting-location-two-hidden').value = displayName;
1011        document.getElementById('mptbm-coordinates-two').value = '';
1012        notifyOSMOperationAreaField('mptbm-starting-location-two-hidden');
1013        notifyOSMOperationAreaField('mptbm-coordinates-two');
1014    });
1015    
1016    // Force Leaflet to recalculate map size (fixes partial rendering)
1017    // Longer delay to ensure container is fully visible
1018    setTimeout(function() {
1019        osmMapTwo.invalidateSize();
1020    }, 500);
1021}
1022
1023// Initialize OSM Map Fixed (Single Operation Area)
1024function InitOSMMapFixed(geoLocation, formattedAddress) {
1025    var mapCanvas3 = document.getElementById('mptbm-map-canvas-three');
1026    if (!mapCanvas3) return;
1027    
1028    // Check if already initialized and clean up
1029    if (osmMapFixed) {
1030        try {
1031            osmMapFixed.remove();
1032        } catch (e) {
1033            console.log('[OSM] Error removing map:', e);
1034        }
1035        osmMapFixed = null;
1036    }
1037    
1038    // Clear Leaflet's internal reference on the container
1039    mapCanvas3._leaflet_id = null;
1040    mapCanvas3.innerHTML = '';
1041    
1042    var defaultLat = geoLocation ? geoLocation.lat : 23.8103;
1043    var defaultLng = geoLocation ? geoLocation.lng : 90.4125;
1044    
1045    osmMapFixed = L.map('mptbm-map-canvas-three').setView([defaultLat, defaultLng], 10);
1046    
1047    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
1048        attribution: '© OpenStreetMap contributors'
1049    }).addTo(osmMapFixed);
1050    
1051    osmDrawLayerFixed = new L.FeatureGroup();
1052    osmMapFixed.addLayer(osmDrawLayerFixed);
1053    
1054    osmDrawControlFixed = new L.Control.Draw({
1055        position: 'topright',
1056        draw: {
1057            polygon: {
1058                allowIntersection: false,
1059                drawError: {
1060                    color: '#e1e100',
1061                    message: '<strong>Error:</strong> Shape edges cannot cross!'
1062                },
1063                shapeOptions: {
1064                    color: '#635bff',
1065                    fillOpacity: 0.5
1066                }
1067            },
1068            polyline: false,
1069            circle: false,
1070            rectangle: false,
1071            marker: false,
1072            circlemarker: false
1073        },
1074        edit: {
1075            featureGroup: osmDrawLayerFixed,
1076            remove: true
1077        }
1078    });
1079    osmMapFixed.addControl(osmDrawControlFixed);
1080    registerOSMOperationAreaMap('mptbm-map-canvas-three', osmMapFixed, osmDrawLayerFixed, osmDrawControlFixed);
1081    
1082    osmMapFixed.on(L.Draw.Event.CREATED, function (e) {
1083        osmDrawLayerFixed.clearLayers();
1084        var layer = e.layer;
1085        osmDrawLayerFixed.addLayer(layer);
1086        saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-three');
1087    });
1088    
1089    osmMapFixed.on(L.Draw.Event.EDITED, function (e) {
1090        var layers = e.layers;
1091        layers.eachLayer(function (layer) {
1092            saveOSMPolygonCoordinates(layer, 'mptbm-coordinates-three');
1093        });
1094    });
1095    
1096    osmMapFixed.on(L.Draw.Event.DELETED, function (e) {
1097        document.getElementById('mptbm-coordinates-three').value = '';
1098        notifyOSMOperationAreaField('mptbm-coordinates-three');
1099    });
1100    
1101    setupOSMLocationSearch('mptbm-starting-location-three', osmMapFixed, function(lat, lng, displayName) {
1102        osmMapFixed.setView([lat, lng], 13);
1103        osmDrawLayerFixed.clearLayers();
1104        document.getElementById('mptbm-starting-location-three-hidden').value = displayName;
1105        document.getElementById('mptbm-coordinates-three').value = '';
1106        notifyOSMOperationAreaField('mptbm-starting-location-three-hidden');
1107        notifyOSMOperationAreaField('mptbm-coordinates-three');
1108    });
1109    
1110    // Force Leaflet to recalculate map size (fixes partial rendering)
1111    // Longer delay to ensure container is fully visible
1112    setTimeout(function() {
1113        osmMapFixed.invalidateSize();
1114    }, 500);
1115}
1116
1117// Save polygon coordinates to hidden input
1118function saveOSMPolygonCoordinates(layer, inputId) {
1119    var coordinates = [];
1120    var latlngs = layer.getLatLngs()[0]; // Get first ring of polygon
1121    
1122    latlngs.forEach(function(latlng) {
1123        coordinates.push(latlng.lat.toFixed(6));
1124        coordinates.push(latlng.lng.toFixed(6));
1125    });
1126    
1127    var inputElement = document.getElementById(inputId);
1128    if (inputElement) {
1129        inputElement.value = coordinates.join(',');
1130        notifyOSMOperationAreaField(inputId);
1131    }
1132}
1133
1134// Load saved polygon onto map
1135function iniOSMSavedMap(coordinates, mapCanvasId, mapAppendId) {
1136    var mapCanvas = document.getElementById(mapCanvasId);
1137    if (!mapCanvas) return;
1138
1139    var slot = String(mapCanvasId).split('-').pop();
1140    var suffix = slot ? slot.charAt(0).toUpperCase() + slot.slice(1) : '';
1141    var existingMap = suffix ? window['osmMap' + suffix] : null;
1142
1143    if (existingMap) {
1144        try {
1145            existingMap.remove();
1146        } catch (e) {
1147            console.log('[OSM] Error removing saved map:', e);
1148        }
1149    }
1150    
1151    // Clear any existing Leaflet instance
1152    if (mapCanvas._leaflet_id) {
1153        mapCanvas._leaflet_id = null;
1154        mapCanvas.innerHTML = '';
1155    }
1156    
1157    // Initialize map
1158    var savedMap = L.map(mapCanvasId).setView([parseFloat(coordinates[0]), parseFloat(coordinates[1])], 10);
1159    
1160    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
1161        attribution: '© OpenStreetMap contributors'
1162    }).addTo(savedMap);
1163    
1164    // Feature group for drawn items
1165    var savedDrawLayer = new L.FeatureGroup();
1166    savedMap.addLayer(savedDrawLayer);
1167    
1168    // Draw control
1169    var savedDrawControl = new L.Control.Draw({
1170        position: 'topright',
1171        draw: {
1172            polygon: {
1173                allowIntersection: false,
1174                shapeOptions: {
1175                    color: '#635bff',
1176                    fillOpacity: 0.5
1177                }
1178            },
1179            polyline: false,
1180            circle: false,
1181            rectangle: false,
1182            marker: false,
1183            circlemarker: false
1184        },
1185        edit: {
1186            featureGroup: savedDrawLayer,
1187            remove: true
1188        }
1189    });
1190    savedMap.addControl(savedDrawControl);
1191    registerOSMOperationAreaMap(mapCanvasId, savedMap, savedDrawLayer, savedDrawControl);
1192    if (slot === 'one' || slot === 'two' || slot === 'three') {
1193        setupOSMLocationSearch('mptbm-starting-location-' + slot, savedMap, function(lat, lng, displayName) {
1194            savedMap.setView([lat, lng], 13);
1195            savedDrawLayer.clearLayers();
1196            var hiddenLocation = document.getElementById('mptbm-starting-location-' + slot + '-hidden');
1197            if (hiddenLocation) {
1198                hiddenLocation.value = displayName;
1199                notifyOSMOperationAreaField(hiddenLocation.id);
1200            }
1201            if (mapAppendId) {
1202                document.getElementById(mapAppendId).value = '';
1203                notifyOSMOperationAreaField(mapAppendId);
1204            }
1205        });
1206    }
1207    
1208    // Convert coordinates array to LatLng array
1209    var latlngs = [];
1210    for (var i = 0; i < coordinates.length; i += 2) {
1211        latlngs.push([parseFloat(coordinates[i]), parseFloat(coordinates[i + 1])]);
1212    }
1213    
1214    // Draw the saved polygon
1215    var polygon = L.polygon(latlngs, {
1216        color: '#635bff',
1217        fillOpacity: 0.5
1218    }).addTo(savedDrawLayer);
1219    
1220    // Fit map to polygon bounds
1221    savedMap.fitBounds(polygon.getBounds());
1222    
1223    // Force map to recalculate size after container is visible
1224    setTimeout(function() {
1225        savedMap.invalidateSize();
1226        savedMap.fitBounds(polygon.getBounds());
1227    }, 300);
1228    
1229    // Handle edits
1230    savedMap.on(L.Draw.Event.EDITED, function (e) {
1231        var layers = e.layers;
1232        layers.eachLayer(function (layer) {
1233            if (mapAppendId) {
1234                saveOSMPolygonCoordinates(layer, mapAppendId);
1235            }
1236        });
1237    });
1238    
1239    savedMap.on(L.Draw.Event.DELETED, function (e) {
1240        if (mapAppendId) {
1241            document.getElementById(mapAppendId).value = '';
1242            notifyOSMOperationAreaField(mapAppendId);
1243        }
1244    });
1245    
1246    savedMap.on(L.Draw.Event.CREATED, function (e) {
1247        savedDrawLayer.clearLayers();
1248        var layer = e.layer;
1249        savedDrawLayer.addLayer(layer);
1250        if (mapAppendId) {
1251            saveOSMPolygonCoordinates(layer, mapAppendId);
1252        }
1253    });
1254}
1255
1256// Setup location search with autocomplete
1257function setupOSMLocationSearch(inputId, map, callback) {
1258    var input = document.getElementById(inputId);
1259    if (!input) return;
1260
1261    if (input._mptbmOsmSearchState) {
1262        input._mptbmOsmSearchState.map = map;
1263        input._mptbmOsmSearchState.callback = callback;
1264        return;
1265    }
1266    
1267    var debounceTimer;
1268    var resultsContainer = document.createElement('div');
1269    resultsContainer.className = 'osm-location-autocomplete';
1270    resultsContainer.style.cssText = 'position: fixed; background: white; border: 1px solid #ddd; border-radius: 4px; max-height: 200px; overflow-y: auto; z-index: 999999; display: none; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);';
1271    
1272    // Append to body to avoid parent overflow issues
1273    document.body.appendChild(resultsContainer);
1274    var searchState = {
1275        map: map,
1276        callback: callback,
1277        resultsContainer: resultsContainer
1278    };
1279    input._mptbmOsmSearchState = searchState;
1280    
1281    // Function to position the dropdown
1282    function positionDropdown() {
1283        var rect = input.getBoundingClientRect();
1284        var top = rect.bottom + 2;
1285        var left = rect.left;
1286        var width = rect.width;
1287        
1288        resultsContainer.style.top = top + 'px';
1289        resultsContainer.style.left = left + 'px';
1290        resultsContainer.style.width = width + 'px';
1291    }
1292    
1293    input.addEventListener('input', function(e) {
1294        clearTimeout(debounceTimer);
1295        var query = e.target.value.trim();
1296        
1297        if (query.length < 3) {
1298            resultsContainer.style.display = 'none';
1299            return;
1300        }
1301        
1302        debounceTimer = setTimeout(function() {
1303            positionDropdown(); // Position before showing
1304            searchOSMLocation(query, resultsContainer, input, searchState.map, searchState.callback);
1305        }, 300);
1306    });
1307    
1308    // Reposition on scroll or resize
1309    document.addEventListener('scroll', function() {
1310        if (resultsContainer.style.display !== 'none') {
1311            positionDropdown();
1312        }
1313    }, true);
1314    
1315    window.addEventListener('resize', function() {
1316        if (resultsContainer.style.display !== 'none') {
1317            positionDropdown();
1318        }
1319    });
1320    
1321    // Hide results when clicking outside
1322    document.addEventListener('click', function(e) {
1323        if (e.target !== input && !resultsContainer.contains(e.target)) {
1324            resultsContainer.style.display = 'none';
1325        }
1326    });
1327}
1328
1329// Search location using Photon API
1330function searchOSMLocation(query, container, input, map, callback) {
1331    var url = 'https://photon.komoot.io/api/?q=' + encodeURIComponent(query) + '&limit=5';
1332    
1333    container.innerHTML = '<div style="padding: 2px; text-align: center; color: #666;">Searching...</div>';
1334    container.style.display = 'block';
1335    
1336    fetch(url)
1337        .then(response => response.json())
1338        .then(data => {
1339            container.innerHTML = '';
1340            
1341            if (!data.features || data.features.length === 0) {
1342                container.innerHTML = '<div style="padding: 2px; color: #666;">No results found</div>';
1343                return;
1344            }
1345            
1346            data.features.forEach(function(feature) {
1347                var properties = feature.properties;
1348                var coordinates = feature.geometry.coordinates;
1349                
1350                var name_parts = [];
1351                if (properties.name) name_parts.push(properties.name);
1352                if (properties.city) name_parts.push(properties.city);
1353                if (properties.state) name_parts.push(properties.state);
1354                if (properties.country) name_parts.push(properties.country);
1355                
1356                var displayName = name_parts.join(', ');
1357                
1358                var item = document.createElement('div');
1359                item.style.cssText = 'padding: 10px; cursor: pointer; border-bottom: 1px solid #eee;';
1360                item.textContent = displayName;
1361                
1362                item.addEventListener('click', function() {
1363                    input.value = displayName;
1364                    container.style.display = 'none';
1365                    
1366                    var lat = coordinates[1];
1367                    var lng = coordinates[0];
1368                    
1369                    if (callback) {
1370                        callback(lat, lng, displayName);
1371                    }
1372                });
1373                
1374                item.addEventListener('mouseenter', function() {
1375                    this.style.backgroundColor = '#f5f5f5';
1376                });
1377                
1378                item.addEventListener('mouseleave', function() {
1379                    this.style.backgroundColor = 'white';
1380                });
1381                
1382                container.appendChild(item);
1383            });
1384        })
1385        .catch(error => {
1386            console.error('Search error:', error);
1387            container.innerHTML = '<div style="padding: 10px; color: #f00;">Search failed</div>';
1388        });
1389}

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.