Spaces:
Running
Running
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Помощь бездомным животным</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> | |
| <style> | |
| :root { | |
| --primary-color: #4CAF50; | |
| --secondary-color: #FF9800; | |
| --dark-color: #333; | |
| --light-color: #f9f9f9; | |
| --danger-color: #F44336; | |
| --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| body { | |
| background-color: #f0f2f5; | |
| color: var(--dark-color); | |
| line-height: 1.6; | |
| } | |
| header { | |
| background-color: var(--primary-color); | |
| color: white; | |
| padding: 1rem 2rem; | |
| box-shadow: var(--shadow); | |
| position: relative; | |
| z-index: 1000; | |
| } | |
| .header-container { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .logo i { | |
| font-size: 1.8rem; | |
| } | |
| .logo h1 { | |
| font-size: 1.5rem; | |
| font-weight: 600; | |
| } | |
| nav ul { | |
| display: flex; | |
| list-style: none; | |
| gap: 20px; | |
| } | |
| nav a { | |
| color: white; | |
| text-decoration: none; | |
| font-weight: 500; | |
| transition: opacity 0.3s; | |
| } | |
| nav a:hover { | |
| opacity: 0.8; | |
| } | |
| .main-container { | |
| display: flex; | |
| max-width: 1200px; | |
| margin: 2rem auto; | |
| gap: 2rem; | |
| padding: 0 1rem; | |
| } | |
| .sidebar { | |
| flex: 1; | |
| background-color: white; | |
| border-radius: 8px; | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow); | |
| height: fit-content; | |
| } | |
| .sidebar h2 { | |
| margin-bottom: 1rem; | |
| color: var(--primary-color); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .sidebar h2 i { | |
| color: var(--secondary-color); | |
| } | |
| .filter-section { | |
| margin-bottom: 1.5rem; | |
| } | |
| .filter-section h3 { | |
| margin-bottom: 0.8rem; | |
| font-size: 1rem; | |
| color: #555; | |
| } | |
| .filter-options { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .filter-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .filter-option input { | |
| cursor: pointer; | |
| } | |
| .filter-option label { | |
| cursor: pointer; | |
| } | |
| select, input[type="range"] { | |
| width: 100%; | |
| padding: 0.5rem; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| margin-bottom: 1rem; | |
| } | |
| .map-container { | |
| flex: 3; | |
| height: 700px; | |
| border-radius: 8px; | |
| box-shadow: var(--shadow); | |
| position: relative; | |
| } | |
| #map { | |
| height: 100%; | |
| border-radius: 8px; | |
| } | |
| .controls { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| z-index: 1000; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| background-color: rgba(255, 255, 255, 0.8); | |
| padding: 10px; | |
| border-radius: 8px; | |
| box-shadow: var(--shadow); | |
| } | |
| .control-btn { | |
| background-color: white; | |
| border: none; | |
| border-radius: 4px; | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: var(--shadow); | |
| transition: all 0.3s; | |
| color: var(--dark-color); | |
| position: relative; | |
| } | |
| .control-btn:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| } | |
| .control-btn.active { | |
| background-color: var(--primary-color); | |
| color: white; | |
| } | |
| .control-btn .tooltip { | |
| position: absolute; | |
| right: 50px; | |
| white-space: nowrap; | |
| background-color: var(--dark-color); | |
| color: white; | |
| padding: 5px 10px; | |
| border-radius: 4px; | |
| font-size: 0.8rem; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.3s; | |
| } | |
| .control-btn:hover .tooltip { | |
| opacity: 1; | |
| } | |
| .control-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| padding: 10px 0; | |
| border-top: 1px solid #eee; | |
| } | |
| .control-group:first-child { | |
| border-top: none; | |
| padding-top: 0; | |
| } | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| z-index: 2000; | |
| justify-content: center; | |
| align-items: center; | |
| animation: fadeIn 0.3s; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .modal-content { | |
| background-color: white; | |
| padding: 2rem; | |
| border-radius: 8px; | |
| width: 90%; | |
| max-width: 500px; | |
| max-height: 90vh; | |
| overflow-y: auto; | |
| position: relative; | |
| box-shadow: var(--shadow); | |
| } | |
| .close-modal { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| color: #777; | |
| } | |
| .close-modal:hover { | |
| color: var(--danger-color); | |
| } | |
| .modal h2 { | |
| margin-bottom: 1.5rem; | |
| color: var(--primary-color); | |
| } | |
| form { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .form-group label { | |
| margin-bottom: 0.5rem; | |
| font-weight: 500; | |
| } | |
| .form-group input, .form-group textarea, .form-group select { | |
| padding: 0.7rem; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| } | |
| .form-group textarea { | |
| resize: vertical; | |
| min-height: 100px; | |
| } | |
| .action-btn { | |
| padding: 0.8rem 1.5rem; | |
| border: none; | |
| border-radius: 4px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| color: white; | |
| } | |
| .btn-primary:hover { | |
| background-color: #3e8e41; | |
| } | |
| .btn-secondary { | |
| background-color: var(--secondary-color); | |
| color: white; | |
| } | |
| .btn-secondary:hover { | |
| background-color: #e68a00; | |
| } | |
| .btn-danger { | |
| background-color: var(--danger-color); | |
| color: white; | |
| } | |
| .btn-danger:hover { | |
| background-color: #d32f2f; | |
| } | |
| .animal-card { | |
| background-color: white; | |
| border-radius: 8px; | |
| padding: 1rem; | |
| margin-bottom: 1rem; | |
| box-shadow: var(--shadow); | |
| display: flex; | |
| gap: 1rem; | |
| cursor: pointer; | |
| transition: transform 0.3s; | |
| } | |
| .animal-card:hover { | |
| transform: translateY(-5px); | |
| } | |
| .animal-card img { | |
| width: 120px; | |
| height: 120px; | |
| object-fit: cover; | |
| border-radius: 6px; | |
| } | |
| .animal-info h3 { | |
| color: var(--primary-color); | |
| margin-bottom: 0.5rem; | |
| } | |
| .animal-info p { | |
| margin-bottom: 0.3rem; | |
| font-size: 0.9rem; | |
| color: #555; | |
| } | |
| .animal-status { | |
| display: inline-block; | |
| padding: 0.2rem 0.5rem; | |
| border-radius: 12px; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| margin-top: 0.5rem; | |
| } | |
| .status-found { | |
| background-color: #e3f2fd; | |
| color: #1976d2; | |
| } | |
| .status-lost { | |
| background-color: #ffebee; | |
| color: #d32f2f; | |
| } | |
| .status-adoption { | |
| background-color: #e8f5e9; | |
| color: #2e7d32; | |
| } | |
| .actions { | |
| display: flex; | |
| gap: 10px; | |
| margin-top: 1rem; | |
| } | |
| .action-btn.small { | |
| padding: 0.5rem 1rem; | |
| font-size: 0.8rem; | |
| } | |
| .map-marker { | |
| background-color: var(--primary-color); | |
| color: white; | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
| transition: transform 0.3s; | |
| } | |
| .map-marker:hover { | |
| transform: scale(1.1); | |
| } | |
| .map-marker i { | |
| font-size: 1.2rem; | |
| } | |
| .recent-activity { | |
| margin-top: 2rem; | |
| } | |
| .activity-item { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 1rem; | |
| padding-bottom: 1rem; | |
| border-bottom: 1px solid #eee; | |
| } | |
| .activity-item img { | |
| width: 50px; | |
| height: 50px; | |
| object-fit: cover; | |
| border-radius: 50%; | |
| } | |
| .activity-content h4 { | |
| font-size: 0.9rem; | |
| margin-bottom: 0.3rem; | |
| } | |
| .activity-content p { | |
| font-size: 0.8rem; | |
| color: #777; | |
| } | |
| .activity-time { | |
| font-size: 0.7rem; | |
| color: #999; | |
| } | |
| @media (max-width: 768px) { | |
| .main-container { | |
| flex-direction: column; | |
| } | |
| .sidebar { | |
| order: 2; | |
| } | |
| .map-container { | |
| order: 1; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="header-container"> | |
| <div class="logo"> | |
| <i class="fas fa-paw"></i> | |
| <h1>Помощь бездомным животным</h1> | |
| </div> | |
| <nav> | |
| <ul> | |
| <li><a href="#"><i class="fas fa-home"></i> Главная</a></li> | |
| <li><a href="#"><i class="fas fa-heart"></i> Взять питомца</a></li> | |
| <li><a href="#"><i class="fas fa-question-circle"></i> Помощь</a></li> | |
| <li><a href="#"><i class="fas fa-user"></i> Войти</a></li> | |
| </ul> | |
| </nav> | |
| </div> | |
| </header> | |
| <div class="main-container"> | |
| <div class="sidebar"> | |
| <h2><i class="fas fa-sliders-h"></i> Фильтры</h2> | |
| <div class="filter-section"> | |
| <h3>Тип животного</h3> | |
| <div class="filter-options"> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-dog" checked> | |
| <label for="filter-dog">Собаки</label> | |
| </div> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-cat" checked> | |
| <label for="filter-cat">Кошки</label> | |
| </div> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-other"> | |
| <label for="filter-other">Другие</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="filter-section"> | |
| <h3>Статус</h3> | |
| <div class="filter-options"> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-found" checked> | |
| <label for="filter-found">Найдены</label> | |
| </div> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-lost" checked> | |
| <label for="filter-lost">Потеряны</label> | |
| </div> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-adoption" checked> | |
| <label for="filter-adoption">На передержке</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="filter-section"> | |
| <h3>Пол</h3> | |
| <div class="filter-options"> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-male" checked> | |
| <label for="filter-male">Мальчик</label> | |
| </div> | |
| <div class="filter-option"> | |
| <input type="checkbox" id="filter-female" checked> | |
| <label for="filter-female">Девочка</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="filter-section"> | |
| <h3>Возраст</h3> | |
| <input type="range" id="age-range" min="0" max="20" value="20"> | |
| <p>До <span id="age-value">20</span> лет</p> | |
| </div> | |
| <div class="filter-section"> | |
| <h3>Район</h3> | |
| <select id="district-select"> | |
| <option value="all">Все районы</option> | |
| <option value="center">Центральный</option> | |
| <option value="north">Северный</option> | |
| <option value="south">Южный</option> | |
| <option value="east">Восточный</option> | |
| <option value="west">Западный</option> | |
| </select> | |
| </div> | |
| <button class="action-btn btn-primary" onclick="applyFilters()"> | |
| <i class="fas fa-filter"></i> Применить фильтры | |
| </button> | |
| <div class="recent-activity"> | |
| <h2><i class="fas fa-history"></i> Последние действия</h2> | |
| <div class="activity-item"> | |
| <img src="https://via.placeholder.com/50" alt="Activity"> | |
| <div class="activity-content"> | |
| <h4>Новый питомец добавлен</h4> | |
| <p>Пёс по кличке Бобик найден в парку Победы</p> | |
| <div class="activity-time">2 часа назад</div> | |
| </div> | |
| </div> | |
| <div class="activity-item"> | |
| <img src="https://via.placeholder.com/50" alt="Activity"> | |
| <div class="activity-content"> | |
| <h4>Обновлён статус</h4> | |
| <p>Кошка Мурка нашла новый дом</p> | |
| <div class="activity-time">Вчера</div> | |
| </div> | |
| </div> | |
| <div class="activity-item"> | |
| <img src="https://via.placeholder.com/50" alt="Activity"> | |
| <div class="activity-content"> | |
| <h4>Новый волонтёр</h4> | |
| <p>Анна присоединилась к нашей команде</p> | |
| <div class="activity-time">3 дня назад</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="map-container"> | |
| <div id="map"></div> | |
| <div class="controls"> | |
| <div class="control-group"> | |
| <button class="control-btn" onclick="zoomIn()"> | |
| <i class="fas fa-plus"></i> | |
| <span class="tooltip">Приблизить</span> | |
| </button> | |
| <button class="control-btn" onclick="zoomOut()"> | |
| <i class="fas fa-minus"></i> | |
| <span class="tooltip">Отдалить</span> | |
| </button> | |
| <button class="control-btn" onclick="locateMe()"> | |
| <i class="fas fa-location-arrow"></i> | |
| <span class="tooltip">Моё местоположение</span> | |
| </button> | |
| </div> | |
| <div class="control-group"> | |
| <button class="control-btn" onclick="toggleAddMode(this)"> | |
| <i class="fas fa-plus-circle"></i> | |
| <span class="tooltip">Добавить животное</span> | |
| </button> | |
| <button class="control-btn" onclick="showLostAnimals()"> | |
| <i class="fas fa-search"></i> | |
| <span class="tooltip">Потерянные</span> | |
| </button> | |
| <button class="control-btn" onclick="showFoundAnimals()"> | |
| <i class="fas fa-home"></i> | |
| <span class="tooltip">Найденные</span> | |
| </button> | |
| </div> | |
| <div class="control-group"> | |
| <button class="control-btn" onclick="showShelters()"> | |
| <i class="fas fa-hotel"></i> | |
| <span class="tooltip">Приюты</span> | |
| </button> | |
| <button class="control-btn" onclick="showVets()"> | |
| <i class="fas fa-clinic-medical"></i> | |
| <span class="tooltip">Ветклиники</span> | |
| </button> | |
| <button class="control-btn" onclick="showVolunteers()"> | |
| <i class="fas fa-hands-helping"></i> | |
| <span class="tooltip">Волонтёры</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Модальное окно добавления животного --> | |
| <div class="modal" id="add-modal"> | |
| <div class="modal-content"> | |
| <span class="close-modal" onclick="closeAddModal()">×</span> | |
| <h2><i class="fas fa-paw"></i> Добавить животное</h2> | |
| <form id="add-animal-form"> | |
| <div class="form-group"> | |
| <label for="animal-type">Тип животного</label> | |
| <select id="animal-type" required> | |
| <option value="">Выберите тип</option> | |
| <option value="dog">Собака</option> | |
| <option value="cat">Кошка</option> | |
| <option value="bird">Птица</option> | |
| <option value="rodent">Грызун</option> | |
| <option value="reptile">Рептилия</option> | |
| <option value="other">Другое</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-name">Кличка</label> | |
| <input type="text" id="animal-name" placeholder="Введите кличку" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-status">Статус</label> | |
| <select id="animal-status" required> | |
| <option value="">Выберите статус</option> | |
| <option value="found">Найдено</option> | |
| <option value="lost">Потеряно</option> | |
| <option value="adoption">Ищет дом</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-gender">Пол</label> | |
| <select id="animal-gender" required> | |
| <option value="">Выберите пол</option> | |
| <option value="male">Мальчик</option> | |
| <option value="female">Девочка</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-age">Возраст (лет)</label> | |
| <input type="number" id="animal-age" min="0" max="30" placeholder="Укажите возраст" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-description">Описание</label> | |
| <textarea id="animal-description" placeholder="Опишите животное, место, где оно найдено/потеряно и т.д." required></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-phone">Контактный телефон</label> | |
| <input type="tel" id="animal-phone" placeholder="+7(XXX)XXX-XX-XX" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="animal-photo">Фото животного</label> | |
| <input type="file" id="animal-photo" accept="image/*"> | |
| </div> | |
| <div class="actions"> | |
| <button type="submit" class="action-btn btn-primary"> | |
| <i class="fas fa-save"></i> Сохранить | |
| </button> | |
| <button type="button" class="action-btn btn-danger" onclick="closeAddModal()"> | |
| <i class="fas fa-times"></i> Отмена | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Модальное окно информации о животном --> | |
| <div class="modal" id="info-modal"> | |
| <div class="modal-content"> | |
| <span class="close-modal" onclick="closeInfoModal()">×</span> | |
| <h2 id="info-title"><i class="fas fa-paw"></i> Информация о животном</h2> | |
| <div id="animal-details"> | |
| <div style="text-align: center; margin-bottom: 1.5rem;"> | |
| <img id="info-photo" src="https://via.placeholder.com/300x200" alt="Animal Photo" style="width: 100%; max-height: 300px; object-fit: cover; border-radius: 6px;"> | |
| </div> | |
| <div style="display: flex; gap: 1rem; margin-bottom: 1rem;"> | |
| <div style="flex: 1;"> | |
| <p><strong>Кличка:</strong> <span id="info-name">Бобик</span></p> | |
| <p><strong>Тип:</strong> <span id="info-type">Собака</span></p> | |
| <p><strong>Пол:</strong> <span id="info-gender">Мальчик</span></p> | |
| </div> | |
| <div style="flex: 1;"> | |
| <p><strong>Возраст:</strong> <span id="info-age">3 года</span></p> | |
| <p><strong>Статус:</strong> <span id="info-status" class="animal-status status-found">Найдено</span></p> | |
| <p><strong>Дата:</strong> <span id="info-date">15.06.2023</span></p> | |
| </div> | |
| </div> | |
| <div style="margin-bottom: 1.5rem;"> | |
| <h3 style="margin-bottom: 0.5rem; color: var(--primary-color);">Описание</h3> | |
| <p id="info-description">Чёрно-белый пёс среднего размера, дружелюбный, найден в парке "Победа" 15 июня. На ошейнике был адресник, но он потерялся. Откликается на кличку Бобик.</p> | |
| </div> | |
| <div style="margin-bottom: 1.5rem;"> | |
| <h3 style="margin-bottom: 0.5rem; color: var(--primary-color);">Контакты</h3> | |
| <p><strong>Телефон:</strong> <a href="tel:+79123456789" id="info-phone">+7 912 345-67-89</a></p> | |
| <p><strong>Email:</strong> <a href="mailto:volunteer@example.com" id="info-email">volunteer@example.com</a></p> | |
| <p><strong>Адрес:</strong> <span id="info-address">Парк "Победа"</span></p> | |
| </div> | |
| <div class="actions"> | |
| <button class="action-btn btn-secondary" onclick="helpWithTransport()"> | |
| <i class="fas fa-car"></i> Помочь с транспортом | |
| </button> | |
| <button class="action-btn btn-primary" onclick="contactVolunteer()"> | |
| <i class="fas fa-phone-alt"></i> Связаться | |
| </button> | |
| <button class="action-btn btn-danger" onclick="reportProblem()"> | |
| <i class="fas fa-flag"></i> Сообщить о проблеме | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> | |
| <script> | |
| // Инициализация карты | |
| let map; | |
| let currentLocationMarker = null; | |
| let animalMarkers = []; | |
| let addModeEnabled = false; | |
| let clickMarker = null; | |
| function initMap() { | |
| // Центр карты - Москва | |
| const center = [55.7522, 37.6156]; | |
| map = L.map('map').setView(center, 12); | |
| // Добавляем слой карты | |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' | |
| }).addTo(map); | |
| // Обработчик кликов на карте | |
| map.on('click', function(e) { | |
| if (addModeEnabled) { | |
| // Если режим добавления включен, добавляем маркер | |
| if (clickMarker) { | |
| map.removeLayer(clickMarker); | |
| } | |
| clickMarker = L.marker(e.latlng, { | |
| icon: L.divIcon({ | |
| className: 'map-marker', | |
| html: '<i class="fas fa-map-marker" style="color: #FF5722"></i>', | |
| iconSize: [40, 40] | |
| }), | |
| draggable: true | |
| }).addTo(map); | |
| clickMarker.on('dragend', function() { | |
| updateMarkerPosition(this.getLatLng()); | |
| }); | |
| // Заполняем адрес в форме | |
| updateAddressFromCoords(e.latlng); | |
| // Открываем модальное окно добавления | |
| openAddModal(); | |
| } | |
| }); | |
| // Добавляем примеры животных | |
| addSampleAnimals(); | |
| } | |
| // Добавление примеров животных на карту | |
| function addSampleAnimals() { | |
| const animals = [ | |
| { | |
| name: "Бобик", | |
| type: "dog", | |
| status: "found", | |
| gender: "male", | |
| age: 3, | |
| description: "Чёрно-белый пёс среднего размера, дружелюбный, найден в парке 'Победа' 15 июня.", | |
| location: [55.7300, 37.6200], | |
| photo: "https://images.unsplash.com/photo-1586671267731-da2cf3ceeb80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80", | |
| date: "15.06.2023", | |
| contact: "+7 912 345-67-89", | |
| email: "volunteer@example.com", | |
| address: "Парк Победы, рядом с детской площадкой" | |
| }, | |
| { | |
| name: "Мурка", | |
| type: "cat", | |
| status: "adoption", | |
| gender: "female", | |
| age: 2, | |
| description: "Серая кошка с белыми лапками, ласковая, ищет дом.", | |
| location: [55.7400, 37.6500], | |
| photo: "https://images.unsplash.com/photo-1526336024174-e58f5cdd8e13?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80", | |
| date: "01.06.2023", | |
| contact: "+7 923 456-78-90", | |
| email: "catlover@mail.ru", | |
| address: "Приют 'Ласковый кот', ул. Зоологическая, 12" | |
| }, | |
| { | |
| name: "Рекс", | |
| type: "dog", | |
| status: "lost", | |
| gender: "male", | |
| age: 5, | |
| description: "Рыжий терьер, потерялся в районе метро 'Чертаново'. На ошейнике бирка с телефоном.", | |
| location: [55.6400, 37.6000], | |
| photo: "https://images.unsplash.com/photo-1583512603805-3cc6b41f3edb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80", | |
| date: "10.06.2023", | |
| contact: "+7 900 123-45-67", | |
| email: "owner@mail.com", | |
| address: "Чертаново, около ТЦ 'Авеню'" | |
| }, | |
| { | |
| name: "Кеша", | |
| type: "bird", | |
| status: "found", | |
| gender: "male", | |
| age: 1, | |
| description: "Говорящий попугай, улетел из открытого окна. Отзывается на кличку.", | |
| location: [55.6800, 37.5800], | |
| photo: "https://images.unsplash.com/photo-1582771498000-8ad44e01c149?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80", | |
| date: "05.06.2023", | |
| contact: "+7 903 333-44-55", | |
| email: "parrot-lover@mail.com", | |
| address: "Найден на балконе дома по ул. Птичья, д.5" | |
| } | |
| ]; | |
| animals.forEach(animal => { | |
| addAnimalToMap(animal); | |
| }); | |
| } | |
| // Добавление животного на карту | |
| function addAnimalToMap(animal) { | |
| const icon = L.divIcon({ | |
| className: 'map-marker', | |
| html: getMarkerIcon(animal), | |
| iconSize: [40, 40] | |
| }); | |
| const marker = L.marker(animal.location, { | |
| icon: icon, | |
| title: animal.name | |
| }).addTo(map); | |
| marker.animalData = animal; // Сохраняем данные животного в маркере | |
| marker.on('click', function() { | |
| showAnimalInfo(animal); | |
| }); | |
| animalMarkers.push(marker); | |
| return marker; | |
| } | |
| // Получение иконки для маркера | |
| function getMarkerIcon(animal) { | |
| let iconClass, color; | |
| switch(animal.type) { | |
| case 'dog': | |
| iconClass = 'fa-dog'; | |
| break; | |
| case 'cat': | |
| iconClass = 'fa-cat'; | |
| break; | |
| case 'bird': | |
| iconClass = 'fa-dove'; | |
| break; | |
| case 'rodent': | |
| iconClass = 'fa-otter'; | |
| break; | |
| case 'reptile': | |
| iconClass = 'fa-dragon'; | |
| break; | |
| default: | |
| iconClass = 'fa-paw'; | |
| } | |
| switch(animal.status) { | |
| case 'found': | |
| color = '#1976d2'; // синий для найденных | |
| break; | |
| case 'lost': | |
| color = '#d32f2f'; // красный для потерянных | |
| break; | |
| case 'adoption': | |
| color = '#2e7d32'; // зелёный для ищущих дом | |
| break; | |
| default: | |
| color = '#4CAF50'; | |
| } | |
| return `<i class="fas ${iconClass}" style="color: ${color}"></i>`; | |
| } | |
| // Управление картой | |
| function zoomIn() { | |
| map.zoomIn(); | |
| } | |
| function zoomOut() { | |
| map.zoomOut(); | |
| } | |
| function locateMe() { | |
| if (navigator.geolocation) { | |
| navigator.geolocation.getCurrentPosition( | |
| function(position) { | |
| const pos = [position.coords.latitude, position.coords.longitude]; | |
| if (currentLocationMarker) { | |
| map.removeLayer(currentLocationMarker); | |
| } | |
| currentLocationMarker = L.marker(pos, { | |
| icon: L.divIcon({ | |
| className: 'map-marker', | |
| html: '<i class="fas fa-user" style="color: #ff5722"></i>', | |
| iconSize: [40, 40] | |
| }) | |
| }).addTo(map); | |
| map.setView(pos, 15); | |
| }, | |
| function(error) { | |
| alert('Не удалось определить ваше местоположение: ' + error.message); | |
| } | |
| ); | |
| } else { | |
| alert('Геолокация не поддерживается вашим браузером'); | |
| } | |
| } | |
| // Включение/выключение режима добавления животного | |
| function toggleAddMode(button) { | |
| addModeEnabled = !addModeEnabled; | |
| button.classList.toggle('active'); | |
| if (addModeEnabled) { | |
| alert('Режим добавления включен. Кликните на карту, чтобы указать расположение животного.'); | |
| } else { | |
| if (clickMarker) { | |
| map.removeLayer(clickMarker); | |
| clickMarker = null; | |
| } | |
| } | |
| } | |
| // Обновление координат маркера | |
| function updateMarkerPosition(latlng) { | |
| updateAddressFromCoords(latlng); | |
| } | |
| // Получение адреса по координатам | |
| function updateAddressFromCoords(latlng) { | |
| // В реальном приложении здесь будет запрос к геокодеру (например, Яндекс.Карты) | |
| const address = `Широта: ${latlng.lat.toFixed(4)}, Долгота: ${latlng.lng.toFixed(4)}`; | |
| if (document.getElementById('animal-description')) { | |
| const currentDesc = document.getElementById('animal-description').value; | |
| if (!currentDesc.includes(address)) { | |
| document.getElementById('animal-description').value = | |
| `${currentDesc}\n\nКоординаты: ${address}`; | |
| } | |
| } | |
| } | |
| // Фильтры | |
| function applyFilters() { | |
| const animalTypeDogs = document.getElementById('filter-dog').checked; | |
| const animalTypeCats = document.getElementById('filter-cat').checked; | |
| const animalTypeOther = document.getElementById('filter-other').checked; | |
| const statusFound = document.getElementById('filter-found').checked; | |
| const statusLost = document.getElementById('filter-lost').checked; | |
| const statusAdoption = document.getElementById('filter-adoption').checked; | |
| const ageMax = document.getElementById('age-range').value; | |
| // Скрываем все маркеры | |
| animalMarkers.forEach(marker => { | |
| map.removeLayer(marker); | |
| }); | |
| // Показываем только те, что соответствуют фильтрам | |
| animalMarkers.forEach(marker => { | |
| const animal = marker.animalData; | |
| let show = true; | |
| // Проверка типа | |
| if ((animal.type === 'dog' && !animalTypeDogs) || | |
| (animal.type === 'cat' && !animalTypeCats) || | |
| ((animal.type !== 'dog' && animal.type !== 'cat') && !animalTypeOther)) { | |
| show = false; | |
| } | |
| // Проверка статуса | |
| if ((animal.status === 'found' && !statusFound) || | |
| (animal.status === 'lost' && !statusLost) || | |
| (animal.status === 'adoption' && !statusAdoption)) { | |
| show = false; | |
| } | |
| // Проверка возраста | |
| if (animal.age > ageMax) { | |
| show = false; | |
| } | |
| if (show) { | |
| marker.addTo(map); | |
| } | |
| }); | |
| } | |
| document.getElementById('age-range').addEventListener('input', function() { | |
| document.getElementById('age-value').textContent = this.value; | |
| }); | |
| // Показать только потерянных животных | |
| function showLostAnimals() { | |
| animalMarkers.forEach(marker => { | |
| map.removeLayer(marker); | |
| if (marker.animalData.status === 'lost') { | |
| marker.addTo(map); | |
| } | |
| }); | |
| map.setView([55.7522, 37.6156], 12); | |
| } | |
| // Показать только найденных животных | |
| function showFoundAnimals() { | |
| animalMarkers.forEach(marker => { | |
| map.removeLayer(marker); | |
| if (marker.animalData.status === 'found') { | |
| marker.addTo(map); | |
| } | |
| }); | |
| map.setView([55.7522, 37.6156], 12); | |
| } | |
| // Показать приюты (заглушка) | |
| function showShelters() { | |
| alert('В этой версии приюты не добавлены. В полной версии будут отображены все приюты в выбранном районе.'); | |
| } | |
| // Показать ветклиники (заглушка) | |
| function showVets() { | |
| alert('В этой версии ветклиники не добавлены. В полной версии будут отображены все ветклиники в выбранном районе.'); | |
| } | |
| // Показать волонтёров (заглушка) | |
| function showVolunteers() { | |
| alert('В этой версии волонтёры не добавлены. В полной версии будут отображены все активные волонтёры в выбранном районе.'); | |
| } | |
| // Модальные окна | |
| function openAddModal() { | |
| document.getElementById('add-modal').style.display = 'flex'; | |
| } | |
| function closeAddModal() { | |
| document.getElementById('add-modal').style.display = 'none'; | |
| if (clickMarker) { | |
| map.removeLayer(clickMarker); | |
| clickMarker = null; | |
| } | |
| addModeEnabled = false; | |
| // Сброс активного состояния кнопки | |
| document.querySelector('.controls .control-btn.active')?.classList.remove('active'); | |
| } | |
| function showAnimalInfo(animal) { | |
| document.getElementById('info-name').textContent = animal.name; | |
| document.getElementById('info-type').textContent = | |
| animal.type === 'dog' ? 'Собака' : | |
| animal.type === 'cat' ? 'Кошка' : | |
| animal.type === 'bird' ? 'Птица' : | |
| animal.type === 'rodent' ? 'Грызун' : | |
| animal.type === 'reptile' ? 'Рептилия' : 'Другое'; | |
| document.getElementById('info-gender').textContent = animal.gender === 'male' ? 'Мальчик' : 'Девочка'; | |
| document.getElementById('info-age').textContent = animal.age + (animal.age === 1 ? ' год' : | |
| animal.age < 5 ? ' года' : ' лет'); | |
| document.getElementById('info-status').textContent = | |
| animal.status === 'found' ? 'Найдено' : | |
| animal.status === 'lost' ? 'Потеряно' : 'Ищет дом'; | |
| document.getElementById('info-date').textContent = animal.date; | |
| document.getElementById('info-description').textContent = animal.description; | |
| document.getElementById('info-photo').src = animal.photo; | |
| document.getElementById('info-phone').textContent = animal.contact; | |
| document.getElementById('info-phone').href = 'tel:' + animal.contact; | |
| document.getElementById('info-email').textContent = animal.email; | |
| document.getElementById('info-email').href = 'mailto:' + animal.email; | |
| document.getElementById('info-address').textContent = animal.address; | |
| // Установка класса статуса | |
| const statusEl = document.getElementById('info-status'); | |
| statusEl.className = 'animal-status status-' + animal.status; | |
| document.getElementById('info-modal').style.display = 'flex'; | |
| } | |
| function closeInfoModal() { | |
| document.getElementById('info-modal').style.display = 'none'; | |
| } | |
| function helpWithTransport() { | |
| const animalName = document.getElementById('info-name').textContent; | |
| alert(`Спасибо за желание помочь с транспортировкой ${animalName}! В ближайшее время с вами свяжется координатор транспорта.`); | |
| } | |
| function contactVolunteer() { | |
| const phone = document.getElementById('info-phone').textContent; | |
| window.location.href = 'tel:' + phone; | |
| } | |
| function reportProblem() { | |
| const animalName = document.getElementById('info-name').textContent; | |
| alert(`Проблема с анкетой ${animalName} будет рассмотрена модератором. Спасибо!`); | |
| } | |
| // Обработка формы добавления животного | |
| document.getElementById('add-animal-form').addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| // Получаем данные из формы | |
| const type = document.getElementById('animal-type').value; | |
| const name = document.getElementById('animal-name').value; | |
| const status = document.getElementById('animal-status').value; | |
| const gender = document.getElementById('animal-gender').value; | |
| const age = parseInt(document.getElementById('animal-age').value); | |
| const description = document.getElementById('animal-description').value; | |
| const phone = document.getElementById('animal-phone').value; | |
| const date = new Date().toLocaleDateString('ru-RU'); | |
| // Получаем координаты из маркера | |
| const position = clickMarker.getLatLng(); | |
| // Создаем объект животного | |
| const newAnimal = { | |
| name: name, | |
| type: type, | |
| status: status, | |
| gender: gender, | |
| age: age, | |
| description: description, | |
| location: [position.lat, position.lng], | |
| photo: "https://via.placeholder.com/300x200?text=" + name, | |
| date: date, | |
| contact: phone, | |
| email: "user@example.com", | |
| address: "Координаты: " + position.lat.toFixed(4) + ", " + position.lng.toFixed(4) | |
| }; | |
| // Добавляем на карту | |
| const marker = addAnimalToMap(newAnimal); | |
| marker.openPopup(); | |
| // Закрываем модальное окно | |
| closeAddModal(); | |
| // Сбрасываем форму | |
| this.reset(); | |
| // Показываем сообщение об успехе | |
| alert('Животное успешно добавлено на карту! После проверки модератором информация будет доступна другим пользователям.'); | |
| }); | |
| // Инициализация карты после загрузки страницы | |
| window.onload = initMap; | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> | |
| </html> |