PageSourceSearch

https://www.novatek.ru/common/js/spg-map.js

js novatek.ru collected 2026-10-02 04:20:17 UTC 40,334 bytes, 1,017 lines download raw bytes

1ymaps.ready(init);
2
3
4var massData = [];
5
6var myMap, myCollection, objectManager, searchControl;
7var currentRoute;
8var buttonClear;
9var imhere;
10var headerOffset = 0;
11var userX = 55.76;
12var userY = 37.64;
13var navigateRequest = false;
14var lastMarkerPosX = 55.75370903771494;
15var lastMarkerPosY = 37.61981338262558;
16var fuelList = [];
17var propertiesList = [];
18var radiusSearch = 0;
19var oilData = [];
20var activeDevice = '';
21var sortActive = false;
22var sortCoordinats = false;
23var itemRouteCar = [];
24var userAddress = '';
25var activeSearchOtherOil = false;
26var activeSearchOtherOilAll = false;
27var routeNewCarBuild = true;
28
29function sortList() {
30    sortActive = !sortActive;
31    if (sortActive) {
32        document.getElementById('sort-abc').style.opacity = 1;
33    } else {
34        document.getElementById('sort-abc').style.opacity = 0.5;
35    }
36    createListAzs()
37}
38
39function myLocationRoute(id) {
40    showDeviceLocation()
41    itemRouteCar[id] = {
42        id: itemRouteCar[id].id,
43        value: 'Моё местоположение',
44        coords: [userX, userY],
45        deviceId: 0
46    }
47    loadDataRouteForm(itemRouteCar.length)
48    addRoute()
49}
50
51function openRouteCar() {
52    document.getElementById('route-car-items').innerHTML = '';
53    //  document.getElementById('list-azs-block').style.display = 'none';
54    document.getElementById('map-route-car').style.display = 'block';
55    itemRouteCar = [];
56
57    addItemRouteCar()
58}
59
60function changeRouteCarName(countRouteCar) {
61    var val = document.getElementById('route-car-item' + countRouteCar).value
62    searchControl.search(val).then(function() {
63        var geoObjectsArray = searchControl.getResultsArray();
64        if (geoObjectsArray.length) {
65            document.getElementById('route-car-item' + countRouteCar).value = geoObjectsArray[0].properties.get('text')
66            itemRouteCar[countRouteCar - 1].coords = geoObjectsArray[0].geometry?.getCoordinates();
67            itemRouteCar[countRouteCar - 1].value = geoObjectsArray[0].properties.get('text');
68
69            objectManager.remove(99999 + '' + countRouteCar);
70            var data = [{
71                'type': 'Feature',
72                'id': 99999 + '' + countRouteCar,
73                'geometry': {'type': 'Point', 'coordinates': itemRouteCar[countRouteCar - 1].coords},
74                'options': {
75                    iconImageHref: '/common/img/spg-map/search.png', iconImageSize: [85, 85],
76                    iconImageOffset: [-42.5, -33]
77                }
78            }]
79            myMap.setCenter(itemRouteCar[countRouteCar - 1].coords, 15);
80            objectManager.add(data);
81
82        } else {
83            alert('Адрес не найден')
84        }
85    });
86
87}
88
89function otherOilChange() {
90    activeSearchOtherOil = !activeSearchOtherOil;
91    document.getElementById('other-oil-search').checked = activeSearchOtherOil;
92}
93
94function otherOilChangeAll() {
95    activeSearchOtherOilAll = !activeSearchOtherOilAll;
96    document.getElementById('other-oil-search').checked = activeSearchOtherOilAll;
97    if (activeSearchOtherOilAll) {
98        activeSearchOtherOil = true;
99        document.getElementById('other-oil-search').checked = activeSearchOtherOil;
100    }
101}
102
103function addItemStopRouteCar(val = '', coords = [], deviceId = 0) {
104    var countRouteCar = itemRouteCar.length
105    var endPoint = JSON.parse(JSON.stringify(itemRouteCar[countRouteCar - 1]))
106
107    itemRouteCar[countRouteCar - 1] = {
108        id: countRouteCar,
109        value: val,
110        coords: coords,
111        deviceId: deviceId
112    }
113    countRouteCar = countRouteCar + 1;
114    endPoint.id = countRouteCar;
115    itemRouteCar.push(endPoint)
116
117    loadDataRouteForm(countRouteCar)
118}
119
120function upRouteCar(id) {
121    var oldPoint = JSON.parse(JSON.stringify(itemRouteCar[id]));
122    var newPoint = JSON.parse(JSON.stringify(itemRouteCar[id - 1]))
123    newPoint.id = oldPoint.id;
124    oldPoint.id = oldPoint.id - 1;
125
126    itemRouteCar[id - 1] = oldPoint;
127    itemRouteCar[id] = newPoint;
128    loadDataRouteForm(itemRouteCar.length)
129}
130
131function removeRouteCar(id) {
132    var newMass = [];
133    var idNew = 1;
134    objectManager.remove(99999 + '' + id);
135    for ( var i = 0; i < itemRouteCar.length; i++ ) {
136        if (itemRouteCar[i].id !== id) {
137            newMass.push({
138                id: idNew,
139                value: itemRouteCar[i].value,
140                coords: itemRouteCar[i].coords
141            })
142            idNew = idNew + 1;
143        } else {
144            if (id !== 1 && idNew !== 1) {
145                idNew = idNew - 1;
146            }
147        }
148    }
149    itemRouteCar = newMass;
150    loadDataRouteForm(itemRouteCar.length)
151}
152
153function loadDataRouteForm(countRouteCar) {
154    document.getElementById('route-car-items').innerHTML = '';
155    for ( var i = 0; i < countRouteCar; i++ ) {
156        var upItemSendRoute = '';
157        if (i != 0) {
158            upItemSendRoute = '<div class="btn-action-route-search" onclick="upRouteCar(' + i + ')">↑</div>'
159        }
160        var disabledInput = 'disabled';
161
162        document.getElementById('route-car-items').innerHTML = document.getElementById('route-car-items').innerHTML +
163            '<div class="search-route-actions">' +
164            '<input type="text" class="input-search-list" ' + disabledInput + ' value="' + itemRouteCar[i].value + '"' +
165            'placeholder="Точка ' + itemRouteCar[i].id + '" id="route-car-item' + itemRouteCar[i].id + '"' +
166            'onchange="changeRouteCarName(' + itemRouteCar[i].id + ')"><div class="d-flex align-items-center">' + upItemSendRoute +
167            '<div class="btn-action-route-search-my" onclick="myLocationRoute(' + i + ')">я</div> ' +
168            '<div class="btn-action-route-search" onclick="removeRouteCar(' + itemRouteCar[i].id + ')">✕</div></div></div>'
169
170    }
171}
172
173function addItemRouteCar(val = '', coords = [], deviceId = 0) {
174    var countRouteCar = itemRouteCar.length
175    countRouteCar = countRouteCar + 1;
176    itemRouteCar.push({
177        id: countRouteCar,
178        value: val,
179        coords: coords,
180        deviceId: deviceId
181    })
182    loadDataRouteForm(countRouteCar)
183}
184
185function newRouteCar(adres, x1, y1, deviceId) {
186    itemRouteCar = [];
187    addItemRouteCar('Моё местоположение', [userX, userY])
188    addItemRouteCar(adres, [x1, y1], deviceId)
189    document.getElementById('map-route-car').style.display = 'block';
190    routeNewCarBuild = true
191    addRoute()
192}
193
194function closeRouteCar() {
195    document.getElementById('map-route-car').style.display = 'none';
196    removeMap();
197    clearRoute();
198    //   document.getElementById('list-azs-block').style.display = 'block';
199}
200
201function sortListCoords() {
202    sortCoordinats = !sortCoordinats;
203    if (sortCoordinats) {
204        document.getElementById('sort-coords').style.opacity = 1;
205    } else {
206        document.getElementById('sort-coords').style.opacity = 0.5;
207    }
208    createListAzs()
209}
210
211function exportList(type) {
212    data = {list: type}
213    $.ajax({
214        url: '/map/export-list',
215        data: data,
216        method: 'GET',
217        error: function(e) {
218            console.log('error_export', e);
219        },
220        success: function(json) {
221
222        }
223    });
224}
225
226//This function takes in latitude and longitude of two location and returns the distance between them as the crow flies (in km)
227function calcCrow(lat1, lon1, lat2, lon2) {
228    var R = 6371; // km
229    var dLat = toRad(lat2 - lat1);
230    var dLon = toRad(lon2 - lon1);
231    var lat1 = toRad(lat1);
232    var lat2 = toRad(lat2);
233
234    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
235        Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
236    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
237    var d = R * c;
238    return d;
239}
240
241// Converts numeric degrees to radians
242function toRad(Value) {
243    return Value * Math.PI / 180;
244}
245
246
247function createListAzs() {
248    var ul = document.getElementById('list-azs');
249    var searchList = document.getElementById('search-azs').value;
250
251
252    ul.innerHTML = '';
253    var itemsFilter = JSON.parse(JSON.stringify(oilData));
254
255    if (searchList) {
256        itemsFilter = oilData.filter(item => item.Name.toLowerCase().indexOf(searchList.toLowerCase()) !== -1);
257    }
258    if (sortActive) {
259        itemsFilter = itemsFilter.sort((a, b) => {
260            if (a.Name?.toLowerCase() < b.Name?.toLowerCase()) {
261                return -1;
262            }
263            if (a.Name?.toLowerCase() > b.Name?.toLowerCase()) {
264                return 1;
265            }
266            return 0;
267        });
268    }
269    if (sortCoordinats) {
270        itemsFilter = itemsFilter.sort(function(a, b) {
271            return a.rast - b.rast;
272        });
273
274    }
275
276    $(itemsFilter).each(function(i, d) {
277        var li = document.createElement('li');
278        var nameAzs = d.Name;
279        if (sortCoordinats) {
280            nameAzs = nameAzs + ' (' + d.rast + ' км.)';
281        }
282        if (d.build) {
283            var em = document.createElement('em');
284            em.appendChild(document.createTextNode(nameAzs))
285            li.appendChild(em);
286        } else {
287            li.appendChild(document.createTextNode(nameAzs));
288        }
289        li.setAttribute('onclick', `openItemMap("${d.DeviceId}")`)
290        if (activeDevice == d.DeviceId) {
291            li.setAttribute('class', `text-danger`);
292            myMap.setCenter([d.DeviceLat, d.DeviceLon], 14);
293
294            var iconMarker = '/common/img/spg-map/station.png';
295            if (d.build == 0) {
296                iconMarker = '/common/img/spg-map/marker.png';
297                if (d.DeviceType == 5) {
298                    iconMarker = '/common/img/spg-map/marker-light.png';
299                }
300            }
301
302            var data = [{
303                'type': 'Feature',
304                'id': d.DeviceId,
305                'geometry': {'type': 'Point', 'coordinates': [d.DeviceLat, d.DeviceLon]},
306                'options': {iconImageHref: iconMarker,},
307                'properties': createBallon(d)
308            }]
309
310
311            objectManager.add(data);
312        }
313        ul.appendChild(li);
314    })
315    if (activeDevice) {
316
317
318        objectManager.objects.balloon.open(activeDevice);
319        objectManager.objects.setObjectOptions(activeDevice,
320            {iconImageHref: '/common/img/spg-map/marker-red.png'});
321    }
322}
323
324function openList() {
325    document.getElementById('list-azs-block').style.display = 'block';
326    document.getElementById('map-filter-action').style.display = 'none';
327}
328
329function closeList() {
330    document.getElementById('list-azs-block').style.display = 'none';
331    document.getElementById('map-filter-action').style.display = 'flex';
332}
333
334function changeRaduis(e) {
335    var val = document.getElementById('radius-filter').value;
336    radiusSearch = val;
337    if (val == 0) {
338        val = '∞'
339    }
340    document.getElementById('radius-value').innerHTML = val
341}
342
343function setHeaderOffset(offset) {
344    headerOffset = offset;
345}
346
347function getLastMarkerPosX() {
348    return lastMarkerPosX;
349}
350
351function getLastMarkerPosY() {
352    return lastMarkerPosY;
353}
354
355function checkBalloon() {
356    return myMap.balloon.isOpen();
357}
358
359function closeBalloon() {
360    myMap.balloon.close()
361}
362
363function checkNavigateRequest() {
364    if (navigateRequest === true) {
365        navigateRequest = false;
366        return true;
367    }
368    return false;
369}
370
371function test() {
372    checkNavigateRequest();
373}
374
375function setUserGeo(x, y) {
376    userX = x;
377    userY = y;
378    const lat = 0;
379    const lon = 0;
380    var imhereTemp = new ymaps.Placemark([x, y],
381        {
382            balloonContent: 'Мое местоположение',
383        },
384        {
385            iconLayout: 'default#image',
386            iconImageHref: '/common/img/spg-map/yandexindex.svg',
387            iconImageSize: [30, 30],
388            iconImageOffset: [-15, -15],
389        }
390    );
391    myMap.geoObjects.remove(imhere);
392    imhere = imhereTemp;
393    myMap.geoObjects.add(imhere);
394
395    if (lat != 0 && lon != 0) {
396        myMap.setCenter([lat, lon], 10);
397    } else {
398        if (userX != null && userY != null) {
399            myMap.setCenter([userX, userY], 10);
400        }
401    }
402}
403
404
405function init_Placemarks() {
406
407    $.ajax({
408        url: '/common/js/spg-map.json',
409        dataType: 'json',
410        method: 'GET',
411        error: function(e) {
412            console.log('ajax_devices_list', e);
413        },
414        success: function(json) {
415            oilData = json
416            $(json).each(function(i, d) {
417                d.rast = Number(calcCrow(userX, userY, d.DeviceLat, d.DeviceLon).toFixed(1))
418                addFilterData(d);
419            });
420            viewData();
421            createListAzs();
422
423            $('#ballon_id').parent().parent().css('overflow-y', 'hidden');
424
425            $('[class$="balloon_layout_panel"]').each(function() {
426                $(this).height($(this).parent().height);
427                $(this).css({'left': '0', 'max-width': '480px', 'min-width': '300px'});
428            });
429
430            $(window).resize(function() {
431                $('#ballon_id').parent().parent().css('overflow-y', 'hidden');
432
433                $('[class$="balloon_layout_panel"]').each(function() {
434                    $(this).height($(this).parent().height);
435                    $(this).css({'left': '0', 'max-width': '480px', 'min-width': '300px'});
436                });
437            });
438        }
439    });
440}
441
442function viewData() {
443    var data = []
444    const fuelActive = fuelList.filter(el => el.active == true)
445    const propActive = propertiesList.filter(el => el.active == true)
446
447
448    $(oilData).each(function(i, d) {
449        var iconMarker = '/common/img/spg-map/station.png';
450        if (d.build == 0) {
451            iconMarker = '/common/img/spg-map/marker.png';
452            if (d.DeviceType == 5) {
453                iconMarker = '/common/img/spg-map/marker-light.png';
454            }
455        }
456
457
458        var ok = 1;
459        // $(fuelActive).each(function (k, m) {
460        //     if(d.FuelList.find(el => el.OilName == m.name)){
461        //         ok = 1;
462        //     }
463        // });
464        // $(propActive).each(function (k, m) {
465        //     if(d.Properties.find(el => el.Order == m.id)){
466        //         ok = 1;
467        //     }
468        // });
469
470
471        data.push({
472            'type': 'Feature',
473            'id': d.DeviceId,
474            'geometry': {'type': 'Point', 'coordinates': [d.DeviceLat, d.DeviceLon]},
475            'options': {iconImageHref: iconMarker,},
476            'properties': createBallon(d)
477        })
478
479    });
480    objectManager.add(data);
481
482}
483
484function createBallon(data) {
485    var x = data.DeviceLat;
486    var y = data.DeviceLon;
487
488    var BalloonContentHeader = '<img style="padding-top: ' + headerOffset + 'px;" src="/common/img/spg-map/back.svg" onclick="myMap.balloon.close();">';
489    var style = '';
490    var image = data.Image ? data.Image : '/storage/';
491    if (data.build == 1) {
492        style = '    filter: invert(27%) sepia(73%) saturate(4786%) hue-rotate(336deg) brightness(81%) contrast(89%);'
493        image = '/common/img/spg-map/tech_work.jpg'
494    }
495
496    var BalloonContent = `
497                <div id='ballon_id' >
498                    <div class='header-wrapper'>
499                        <span class='oil-station-name'>${data.Name}</span>
500                    </div>
501                    <div class='meta-wrapper'>
502                        <ul class='meta-list'>
503                            <li class='meta-item-phone'>
504                                ${data.DevicePhone}
505                            </li>
506                            <li class='meta-item-address'>
507                                ${data.DeviceAddress}
508                            </li>
509                             <li class='meta-item-text-oil' style='${style}'>
510                                ${data.build == 1 ? '<em>Станция находится в процессе строительства или ремонта</em>' : data.DeviceInfo}
511                            </li>
512                        </ul>
513                    </div>
514                    <div class='oil-wrapper ${data.build == 1 ? 'd-none' : ''}'>
515                        ${getOilTypes(data.FuelList)}
516                    </div>
517                    <div class='services-wrapper '>
518                        ${getServices(data.Properties)}
519                    </div>
520                </div>
521                `;
522
523
524    var BalloonContentFooter = '<div height=10%><table width="100%";  border="0"; cellpadding="4";><tr align="center">' +
525        '<td><button style="width:45%; font-size: 2vh;" class="btn" onclick="newRouteCar(`' + data.Name + '`, ' + x + ', ' + y + ', ' + data.DeviceId + '); myMap.balloon.close()">МАРШРУТ</button></td>' +
526        //'<td><button  style="width:90%;  font-size: 2vh;" class="btn" onclick="navigateRequest = true; myMap.balloon.close();">НАВИГАТОР</button></td>';
527        '</tr></table></div>'
528
529
530    return {
531        'balloonContentHeader': BalloonContentHeader,
532        'balloonContentBody': BalloonContent,
533        'balloonContentFooter': BalloonContentFooter,
534        'hintContent': data.Name
535    }
536}
537
538function openFilters() {
539    changeRaduis()
540    document.getElementById('map-filters').style = 'block'
541    const htmlFuel = document.getElementById('all-fuel-list');
542    var fuelData = '<h5>Топливо</h5>';
543    $(fuelList).each(function(i, d) {
544        fuelData += '<div class="item-filter-option"><span>' + d.name + '</span><input type="checkbox" onchange="changeFuelFilterData(`' + d.name + '`)" ' + (d.active ? 'checked="checked"' : '') + '"></div>'
545    });
546    htmlFuel.innerHTML = fuelData;
547
548    const htmlProp = document.getElementById('all-prop-list');
549    var propData = '<h5>Услуги</h5>';
550    $(propertiesList).each(function(i, d) {
551        propData += '<div class="item-filter-option"><span>' + d.name + '</span><input type="checkbox" onchange="changePropFilterData(`' + d.id + '`)" ' + (d.active ? 'checked="checked"' : '') + '"></div>'
552    });
553    htmlProp.innerHTML = propData;
554}
555
556function openItemMap(item) {
557    var iconMarker = '';
558    if (activeDevice) {
559        var oldDevice = oilData.find(el => el.DeviceId == activeDevice)
560
561        iconMarker = '/common/img/spg-map/station.png';
562        if (oldDevice.build == 0) {
563            iconMarker = '/common/img/spg-map/marker.png';
564            if (oldDevice.DeviceType == 5) {
565                iconMarker = '/common/img/spg-map/marker-light.png';
566            }
567        }
568
569        objectManager.objects.setObjectOptions(activeDevice, {iconImageHref: iconMarker})
570    }
571
572    activeDevice = item;
573
574
575    createListAzs()
576}
577
578function changeFuelFilterData(e) {
579    fuelList.find(el => el.name == e).active = !fuelList.find(el => el.name == e).active
580}
581
582function changePropFilterData(e) {
583    propertiesList.find(el => el.id == e).active = !propertiesList.find(el => el.id == e).active
584}
585
586function addFilterData(item) {
587    if (item.FuelList.length) {
588        $(item.FuelList).each(function(i, d) {
589            if (!fuelList.find(el => el.name == d.Code)) {
590                fuelList.push({
591                    name: d.Code,
592                    active: true
593                })
594            }
595        });
596    }
597    if (item.Properties.length) {
598        $(item.Properties).each(function(i, d) {
599            if (!propertiesList.find(el => el.id == d.Order)) {
600                propertiesList.push({
601                    id: d.Order,
602                    name: d.PropName,
603                    active: true
604                })
605            }
606        });
607    }
608}
609
610function init() {
611    const point = [55.76, 37.64]; // Москва
612    const zoom = 10;
613    searchControl = new ymaps.control.SearchControl({
614        // Опция отключает автоматический выбор результата поиска.
615        options: {noSelect: true}
616    });
617
618    myMap = new ymaps.Map(
619        'map',
620        { // Создание экземпляра карты и его привязка к контейнеру с заданным id ("map")
621            center: point, // Москва
622            zoom: zoom,
623            controls: []
624        },
625        {
626            suppressMapOpenBlock: true
627        }),
628        objectManager = new ymaps.ObjectManager({
629            // Чтобы метки начали кластеризоваться, выставляем опцию.
630            clusterize: true,
631            // ObjectManager принимает те же опции, что и кластеризатор.
632            gridSize: 100,
633
634            options: {
635                iconImageHref: '/common/img/spg-map/baloon.png',
636            }
637            // clusterDisableClickZoom: true
638        });
639
640    myMap.geoObjects.add(objectManager);
641
642    objectManager.objects.options.set({// Опции
643        // Необходимо указать данный тип макета.
644        iconLayout: 'default#image',
645        // Своё изображение иконки метки.
646
647        // Размеры метки.
648        iconImageSize: [85, 85],
649        iconImageOffset: [-42.5, -33],
650
651        balloonCloseButton: false, //Кнопка закрыть на баллуне есть
652        balloonPanelMaxMapArea: 'Infinity', //Баллун открывается в виде панели снизу
653        balloonMinHeight: document.body.clientHeight - 22
654    });
655
656    myMap.controls.add('zoomControl', {
657        size: 'small',
658        float: 'none',
659        position: {
660            top: '40px',
661            right: '15px'
662        }
663    });
664
665    buttonClear = new ymaps.control.Button({
666        data: {
667            image: '/common/img/spg-map/close.svg'
668        },
669        options: {
670            maxWidth: [40]
671        }
672    });
673
674    buttonClear.events.add('click', function(e) {
675        clearRoute();
676    });
677
678    // buttonFocus.events.add('click', function (e) {
679    //     focusToUser();
680    // });
681
682    showDeviceLocation();
683    init_Placemarks();
684}
685
686function getOilTypes(fuelList) {
687    this.list = fuelList;
688
689    let got = this;
690    let output = ``;
691    $(got.list).each(function(i, d) {
692        output += `
693                    <div class='oiltype-container'>
694                        <span class='fuel-name'>
695                        ${(d.Code == null ? d.OilName : d.Code)}
696                        </span>
697                        <span class='fuel-price'>
698                            ${parseFloat(d.oilprice).toFixed(2)}
699                        </span>
700                    </div>
701                `;
702    });
703
704    return output;
705}
706
707function getServices(servicesList) {
708    this.list = servicesList;
709
710    let gs = this;
711    let output = `<ul class='services-list'>`;
712    $(gs.list).each(function(i, d) {
713        output += `
714                    <li style="background-image: url('${d.Image}')">
715                        ${d.PropName}
716                    </li>
717                `;
718    });
719    output += `</ul>`;
720
721    return output;
722}
723
724function resetFilter() {
725    objectManager.removeAll()
726    showDeviceLocation();
727    init_Placemarks();
728    document.getElementById('map-filters').style.display = 'none'
729}
730
731function closeFilters() {
732    document.getElementById('map-filters').style.display = 'none'
733}
734
735
736function removeMap() {
737    objectManager.removeAll()
738    showDeviceLocation();
739    viewData()
740    document.getElementById('map-filters').style.display = 'none'
741}
742
743function addPlacemark(data) { //Установка маркера
744    var x = data.DeviceLat;
745    var y = data.DeviceLon;
746
747    var placemark = new ymaps.Placemark([x, y],
748        {
749            balloonContent: ' ',
750        },
751        {// Опции
752            // Необходимо указать данный тип макета.
753            iconLayout: 'default#image',
754            // Своё изображение иконки метки.
755            iconImageHref: '/common/img/spg-map/itoilmarker.png',
756            // Размеры метки.
757            iconImageSize: [34, 34],
758            // Смещение левого верхнего угла иконки относительно
759            // её "ножки" (точки привязки).
760            iconImageOffset: [-17, -34],
761            balloonCloseButton: false, //Кнопка закрыть на баллуне есть
762            balloonPanelMaxMapArea: 'Infinity', //Баллун открывается в виде панели снизу
763            balloonMinHeight: document.body.clientHeight - 22
764        }
765    );
766
767    placemark.events.add('balloonopen', function(e) {
768        lastMarkerPosX = x;
769        lastMarkerPosY = y;
770        navigateRequest = false;
771
772
773        placemark.properties.set('balloonContent', 'Идет загрузка данных...');
774        var BalloonContentHeader = '<img style="padding-top: ' + headerOffset + 'px;" src="/common/img/spg-map/back.svg" onclick="myMap.balloon.close();">';
775
776        var BalloonContent = `
777                <div id='ballon_id' style='overflow-y: scroll; height: 85%; overflow-x: hidden;'>
778                    <div class='header-wrapper'>
779                        <span class='oil-station-name'>${data.Name}</span>
780                    </div>
781                    <div class='image-wrapper'>
782                        <img src='${data.Image ? data.Image : '/storage/'}' />
783                    </div>
784                    <div class='meta-wrapper'>
785                        <ul class='meta-list'>
786                            <li class='meta-item-phone'>
787                                ${data.DevicePhone}
788                            </li>
789                            <li class='meta-item-address'>
790                                ${data.DeviceAddress}
791                            </li>
792                             <li class='meta-item-text-oil'>
793                                ${data.DeviceInfo}
794                            </li>
795                        </ul>
796                    </div>
797                    <div class='oil-wrapper'>
798                        ${getOilTypes(data.FuelList)}
799                    </div>
800                    <div class='services-wrapper'>
801                        ${getServices(data.Properties)}
802                    </div>
803                </div>
804                `;
805        // '<div style="overflow-y: scroll; height: 80%;" >' +
806        // '<strong>' + data.Name + '</strong><br/>' +
807        // '<img src="' + data.Image + '" width="200"><br/>' +
808        // '<svg width="16" height="16" style="margin-bottom: -3px;" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">' +
809        //     '<path d="M14.6367 17.6497L14.6352 17.6497C13.6735 17.645 12.7648 17.3686 11.8442 16.9898L11.8435 16.9895C9.90601 16.1975 8.21441 15.0109 6.62802 13.6238L6.62794 13.6238C5.44638 12.5913 4.37475 11.461 3.42221 10.2182L3.42183 10.2177C2.30204 8.76205 1.34876 7.22868 0.764072 5.5052C0.494674 4.70733 0.314203 3.91915 0.35601 3.11612L0.356051 3.11612L0.356247 3.11077C0.373953 2.62863 0.511594 2.23427 0.847901 1.92984L0.847902 1.92984L0.848683 1.92913C1.0125 1.77994 1.16881 1.62026 1.31648 1.46943C1.35547 1.4296 1.39386 1.39039 1.43162 1.35215C1.61697 1.16446 1.79379 0.993174 1.98475 0.845485C2.25223 0.639614 2.5916 0.473717 2.92707 0.379733C3.23917 0.29438 3.57294 0.398581 3.8889 0.640787L3.8889 0.640789L3.89 0.641626C4.75989 1.30301 5.39315 2.15212 5.79649 3.17159L5.79701 3.1729C6.01051 3.70627 5.89771 4.18534 5.49349 4.64465C5.26534 4.90303 5.05022 5.14516 4.81568 5.34736L4.81568 5.34735L4.81413 5.3487C4.60852 5.52807 4.47455 5.73858 4.41457 5.97831C4.35593 6.21273 4.37471 6.44825 4.4282 6.67094L4.42818 6.67094L4.42899 6.67415C4.58663 7.30427 4.92661 7.83044 5.27633 8.29297L5.27659 8.29331C6.31303 9.66061 7.51437 10.86 8.78846 11.9765C9.41713 12.5293 10.1011 13.0901 10.9463 13.4346C11.1588 13.5229 11.3896 13.5799 11.6115 13.6084L11.6114 13.6085L11.6204 13.6094C11.9725 13.6453 12.2964 13.5317 12.56 13.2683C12.8627 12.9659 13.1521 12.677 13.4634 12.4186C13.8862 12.0686 14.3602 12.0041 14.8723 12.2128L14.8734 12.2133C15.8378 12.6025 16.654 13.1978 17.2906 14.0083L17.2909 14.0087C17.5544 14.3433 17.661 14.672 17.6491 14.9821C17.6371 15.294 17.5035 15.6268 17.2149 15.9637L17.2148 15.9638C16.8116 16.4346 16.3618 16.8749 15.8946 17.307L15.8943 17.3072C15.5844 17.5945 15.1665 17.6546 14.6367 17.6497Z" stroke="#4F5A6B" stroke-width="0.7"/>' +
810        // '</svg> ' +
811        // data.DevicePhone + '<br/><hr/>' +
812        // '<svg width="16" height="16" style="margin-bottom: -3px;" viewBox="0 0 15 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
813        //     '<path d="M10.8101 1.16135L10.8101 1.16134C9.94279 0.701413 9.01609 0.441895 8.03353 0.370763C7.97165 0.366625 7.90733 0.361167 7.8468 0.35603C7.82204 0.353929 7.79791 0.351882 7.77484 0.35H7.77096H7.75296H7.71695H7.64495H7.57294H7.50093H7.49981H7.49868H7.49756H7.49643H7.49531H7.49418H7.49306H7.49193H7.49081H7.48968H7.48855H7.48743H7.4863H7.48518H7.48405H7.48293H7.4818H7.48068H7.47955H7.47843H7.4773H7.47618H7.47505H7.47393H7.4728H7.47168H7.47055H7.46943H7.4683H7.46718H7.46605H7.46493H7.4638H7.46268H7.46155H7.46043H7.4593H7.45818H7.45705H7.45593H7.4548H7.45368H7.45255H7.45143H7.4503H7.44918H7.44805H7.44693H7.4458H7.44468H7.44355H7.44243H7.4413H7.44018H7.43905H7.43793H7.4368H7.43567H7.43455H7.43342H7.4323H7.43117H7.43005H7.42892H7.4278H7.42667H7.42555H7.42442H7.4233H7.42217H7.42105H7.41992H7.4188H7.41767H7.41655H7.41542H7.4143H7.41317H7.41205H7.41092H7.4098H7.40867H7.40755H7.40642H7.4053H7.40417H7.40305H7.40192H7.4008H7.39967H7.39855H7.39742H7.3963H7.39517H7.39405H7.39292H7.3918H7.39067H7.38955H7.38842H7.3873H7.38617H7.38505H7.38392H7.38279H7.38167H7.38054H7.37942H7.37829H7.37717H7.37604H7.37492H7.37379H7.37267H7.37154H7.37042H7.36929H7.36817H7.36704H7.36592H7.36479H7.36367H7.36254H7.36142H7.36029H7.35917H7.35804H7.35692H7.35579H7.35467H7.35354H7.35242H7.35129H7.35017H7.34904H7.34792H7.34679H7.34567H7.34454H7.34342H7.34229H7.34117H7.34004H7.33892H7.33779H7.33667H7.33554H7.33441H7.33329H7.33216H7.33104H7.32991H7.32879H7.32766H7.32654H7.32541H7.32429H
8137.32316H7.32204H7.32091H7.31979H7.31866H7.31754H7.31641H7.31529H7.31416H7.31304H7.31191H7.31079H7.30966H7.30854H7.30741H7.30629H7.30516H7.30404H7.30291H7.30179H7.30066H7.29954H7.29841H7.29729H7.29616H7.29504H7.29391H7.29279H7.29166H7.29054H7.28941H7.28829H7.28716H7.28604H7.28491H7.28378H7.28266H7.28153H7.28041H7.27928H7.27816H7.27703H7.27591H7.27478H7.27366H7.27253H7.27141H7.27028H7.26916H7.26803H7.26691H7.26578H7.26466H7.26353H7.26241H7.26128H7.26016H7.25903H7.25791H7.25678H7.25566H7.25453H7.25341H7.25228H7.25116H7.25003H7.24891H7.24778H7.24666H7.24553H7.24441H7.24328H7.24216H7.24103H7.23991H7.23878H7.23766H7.23725C7.21648 0.352823 7.19303 0.355762 7.16928 0.358025L7.16516 0.358417L7.16516 0.358392C7.09231 0.364464 7.02618 0.368948 6.96319 0.37322C6.85815 0.380343 6.76181 0.386876 6.65748 0.399194L6.65665 0.399291C6.15142 0.457722 5.6584 0.567473 5.18032 0.734809L5.18022 0.734845C3.70448 1.25086 2.51907 2.14537 1.62546 3.42742C0.780217 4.64107 0.362841 5.99167 0.350187 7.47391C0.346777 7.93239 0.390098 8.3885 0.476098 8.84261L0.476108 8.84266C0.593529 9.46319 0.77982 10.0558 1.06094 10.6079C3.0452 14.5032 5.0323 18.3965 7.01978 22.2906C7.18017 22.6049 7.34056 22.9191 7.50094 23.2334C8.20652 21.851 8.9119 20.4685 9.61727 19.0861C11.0145 16.3477 12.4117 13.6094 13.8103 10.8716L13.8103 10.8716C14.1824 10.1435 14.445 9.39072 14.5669 8.59437L10.8101 1.16135ZM10.8101 1.16135C12.3084 1.95582 13.3935 3.12983 14.0737 4.68406C14.3309 5.27252 14.4968 5.889 14.584 6.53052L14.5841 6.53061M10.8101 1.16135L14.5841 6.53061M14.5841 6.53061C14.6779 7.21931 14.6711 7.90688 14.5669 8.59434L14.5841 6.53061ZM7.50333 2.65045C4.82364 2.65045 2.65437 4.81709 2.65049 7.49462C2.64661 10.1695 4.81901 12.3505 7.49236 12.3518C10.1768 12.3531 12.3527 10.1804 12.3514 7.49976C12.3501 4.82365 10.1787 2.65045 7.50333 2.65045Z" stroke="#4F5A6B" stroke-width="0.7"/>' +
814        // '</svg> ' +
815        // data.DeviceAddress + '<br/><hr/>'+
816        // '<div><table width="100%">';
817
818        // var cnt_tr = 0;
819        // var header_line = '';
820        // var price_line = '';
821        // $(data.FuelList).each(function (i, d) {
822        //     if (cnt_tr < 4) {
823        //         cnt_tr++;
824        //         console.log(d.Code);
825        //         header_line += '<th class="fuel">' + (d.Code == null?d.OilName:d.Code) + '</th>';
826        //         price_line += '<td class="fuel_price"><h2>' + parseFloat(d.oilprice).toFixed(2) + '</h2></td>';
827        //     } else {
828        //         BalloonContent += '<tr>' + header_line + '</tr><tr>' + price_line + '</tr>';
829        //         cnt_tr = 1;
830        //         header_line = '<th class="fuel">' + (d.Code == null?d.OilName:d.Code) + '</th>';
831        //         price_line = '<td class="fuel_price"><h2>' + parseFloat(d.oilprice).toFixed(2) + '</h2></td>';
832        //     }
833        // });
834        // BalloonContent += '<tr>' + header_line + '</tr><tr>' + price_line + '</tr>';
835        // BalloonContent += '</table></div><br/><hr/>';
836        //
837        // $(data.Properties).each(function (i, d) {
838        //     BalloonContent += '<img src=" ' + d.Image + '" width="16" style="margin-bottom: -3px;"> <span>' + d.PropName + '</span><br/><hr/>';
839        // });
840        //
841        // BalloonContent += '</div>';
842
843        var BalloonContentFooter = '<div height=10%><table width="100%";  border="0"; cellpadding="4";><tr align="center">' +
844            '<td><button style="width:45%; font-size: 2vh;" class="btn" onclick="newRouteCar(' + userX + ', ' + userY + ', ' + x + ', ' + y + '); myMap.balloon.close()">МАРШРУТ</button></td>' +
845            //'<td><button  style="width:90%;  font-size: 2vh;" class="btn" onclick="navigateRequest = true; myMap.balloon.close();">НАВИГАТОР</button></td>';
846            '</tr></table></div>'
847        // Задаем новое содержимое балуна в соответствующее свойство метки.
848        placemark.properties.set('balloonContentHeader', BalloonContentHeader);
849        placemark.properties.set('balloonContent', BalloonContent);
850        placemark.properties.set('balloonContentFooter', BalloonContentFooter);
851
852        $('#ballon_id').parent().parent().css('overflow-y', 'hidden');
853
854        $('[class$="balloon_layout_panel"]').each(function() {
855            $(this).height($(this).parent().height);
856            $(this).css({'left': '0', 'max-width': '480px', 'min-width': '300px'});
857        });
858
859        $(window).resize(function() {
860            $('#ballon_id').parent().parent().css('overflow-y', 'hidden');
861
862            $('[class$="balloon_layout_panel"]').each(function() {
863                $(this).height($(this).parent().height);
864                $(this).css({'left': '0', 'max-width': '480px', 'min-width': '300px'});
865            });
866        });
867
868    });
869
870    myMap.geoObjects.add(placemark);
871
872
873}
874
875var route_points = [];
876
877function addRoute() { //Построение маршрута
878    var coordsRoute = itemRouteCar?.map(function(value) {
879        return value.coords;
880    });
881    if (coordsRoute.length < 2) {
882        alert('Маршрут не может состоять из одной точки');
883        return 0;
884    }
885    removeMap();
886    clearRoute();
887
888    currentRoute = new ymaps.multiRouter.MultiRoute(
889        {
890            referencePoints: coordsRoute,
891            params: {      // Параметры маршрутизации.
892                results: 1, // Ограничение на максимальное количество маршрутов, возвращаемое маршрутизатором.
893                reverseGeocoding: true
894            }
895        },
896        {
897            editorDrawOver: false,
898            // The editorMidPointsType option sets the type of intermediary
899            // points to create on the route.
900            // "via" — throughpoints
901            // "way" — waypoints
902            editorMidPointsType: 'via',
903            boundsAutoApply: true // Автоматически устанавливать границы карты так, чтобы маршрут был виден целиком.
904        }
905    )
906    myMap.geoObjects.add(currentRoute);
907
908    myMap.controls.add(buttonClear, {
909        float: 'none',
910        position: {
911            top: '120px',
912            right: '15px'
913        }
914    });
915
916    /*Расстояние до маршрута*/
917    var threshold = activeSearchOtherOilAll ? 700 : 200;
918    /*когда маршрут построился без ошибок*/
919    currentRoute.model.events.add('requestsuccess', function() {
920        var lineGeoObjects = currentRoute.getRoutes().toArray().map(route => new ymaps.Polyline(routeToLineString(route)));
921
922        var lines = new ymaps.GeoObjectCollection({children: lineGeoObjects}, {visible: false});
923
924        myMap.geoObjects.add(lines);
925
926        var newCoords = [];
927
928        $.each(oilData, function(index, element) {
929
930            var coords = [element.DeviceLat, element.DeviceLon];
931
932            var isNearSomeRoute = lines.toArray().some(line => {
933                var closest = line.geometry.getClosest(coords);
934                return closest.distance < threshold;
935            });
936            if (!isNearSomeRoute) {
937                objectManager.remove([element.DeviceId]);
938            }
939
940            if (routeNewCarBuild && isNearSomeRoute && (activeSearchOtherOil || activeSearchOtherOilAll) && !itemRouteCar.find(el => el.deviceId == element.DeviceId)) {
941                var newMass = [];
942                var addItem = 0;
943                var ik = 1;
944
945                var dist = activeSearchOtherOilAll ? 0.7 : .2;
946
947                $.each(itemRouteCar, function(ind, itemActiveRoute) {
948                    if (addItem == 1) {
949                        itemActiveRoute.id = ik;
950                    }
951                    if (ind > 0 && addItem === 0 && itemActiveRoute.coords?.length > 1
952                        && element.DeviceLat != itemActiveRoute.coords[0] && element.DeviceLon != itemActiveRoute.coords[1] &&
953                        (Math.abs(element.DeviceLat - itemActiveRoute.coords[0]) < dist && Math.abs(element.DeviceLon - itemActiveRoute.coords[1]) < dist)) {
954                        itemRouteCar.push({
955                            coords: coords,
956                            id: ik,
957                            value: element.Name,
958                            deviceId: element.DeviceId
959                        })
960                        ik = ik + 1;
961                        itemRouteCar[ind] = {
962                            coords: itemActiveRoute.coords,
963                            id: ik,
964                            value: itemActiveRoute.value,
965                            deviceId: itemActiveRoute.deviceId
966                        }
967                        addItem = 1;
968                    }
969                    ik = ik + 1;
970                });
971            }
972        });
973        if (routeNewCarBuild && activeSearchOtherOil) {
974            routeNewCarBuild = false;
975            itemRouteCar.sort((a, b) => parseFloat(a.id) - parseFloat(b.id));
976            loadDataRouteForm(itemRouteCar.length)
977            addRoute()
978        }
979    });
980}
981
982/*Функция которая считает попадает ли точка в диапозон threshold*/
983function routeToLineString(route) {
984    var points = route.getPaths().toArray()
985        .map(x => x.getSegments().toArray())
986        .reduce((s, x) => s.concat(x), [])
987        .map(x => x.geometry.getCoordinates())
988        .reduce((s, x) => s.concat(x), []);
989    return {type: 'LineString', coordinates: points};
990}
991
992function showDeviceLocation() {
993    var geolocation = ymaps.geolocation;
994    geolocation.get({
995        provider: 'auto',
996        mapStateAutoApply: false,
997        autoReverseGeocode: false,
998    }).then(function(result) {
999        // console.log('Yandex auto:' + result.geoObjects.accuracy);
1000        // result.geoObjects.options.set('preset', 'islands#redCircleIcon');
1001        // result.geoObjects.get(0).properties.set({
1002        //     balloonContentBody: 'Мое местоположение'
1003        // });
1004        // myMap.geoObjects.add(result.geoObjects);
1005        // userAddress = result.geoObjects.get(0).properties.get('text');
1006        setUserGeo(result.geoObjects.position[0], result.geoObjects.position[1])
1007    });
1008}
1009
1010function clearRoute() {
1011    myMap.geoObjects.remove(currentRoute);
1012    myMap.controls.remove(buttonClear);
1013}
1014
1015function focusToUser() {
1016    myMap.setCenter([userX, userY], 13);
1017}

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.