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.