fisrt init
|
|
@ -0,0 +1,2 @@
|
|||
/data
|
||||
/.git
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_manager.php';
|
||||
|
||||
use function Catcher\recovery;
|
||||
|
||||
function gen_geojson($name, $features) {
|
||||
return (object)[
|
||||
"type" => "FeatureCollection",
|
||||
"name" => $name,
|
||||
"crs" => (object) [
|
||||
"type" => "name",
|
||||
"properties" => (object) [
|
||||
"name" => "urn:ogc:def:crs:OGC:1.3:CRS84"
|
||||
]
|
||||
],
|
||||
"features" => $features
|
||||
];
|
||||
}
|
||||
function gen_feature($settlement) {
|
||||
$settlement = (object) $settlement;
|
||||
$settlement->info_exist = ($settlement->info_exist == 1)? true : false;
|
||||
if($settlement->slider)
|
||||
$settlement->slider = json_decode($settlement->slider);
|
||||
if($settlement->images)
|
||||
$settlement->images = json_decode($settlement->images);
|
||||
$latitude = $settlement->latitude;
|
||||
$longitude = $settlement->longitude;
|
||||
unset($settlement->latitude);
|
||||
unset($settlement->longitude);
|
||||
|
||||
return (object) [
|
||||
"type" => "Feature",
|
||||
"properties" => $settlement,
|
||||
"geometry" => (object) [
|
||||
"type" => "Point",
|
||||
"coordinates" => [$longitude, $latitude]
|
||||
]
|
||||
];
|
||||
}
|
||||
function gen_json($obj) :string {
|
||||
return json_encode($obj, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
$body_raw = file_get_contents('php://input');
|
||||
$body = json_decode($body_raw);
|
||||
|
||||
$mode = $body->mode;
|
||||
|
||||
$result = (object)[];
|
||||
|
||||
switch($mode) {
|
||||
case 'geojsons':
|
||||
$periods_geojson = (object) [];
|
||||
$settlements = $db_manager->select_all();
|
||||
foreach($settlements as $settlement) {
|
||||
$period = ((object)$settlement)->period;
|
||||
if(empty($periods_geojson->$period))
|
||||
$periods_geojson->$period = [];
|
||||
array_push($periods_geojson->$period, gen_feature($settlement));
|
||||
};
|
||||
|
||||
foreach($periods_geojson as $name=>$features) {
|
||||
$result->$name = gen_geojson($name, $features);
|
||||
}
|
||||
|
||||
echo gen_json($result);
|
||||
break;
|
||||
case 'file':
|
||||
$full_path = $body->path;
|
||||
|
||||
if (is_file($full_path)) {
|
||||
// Определяем MIME-тип (например, для видео/mp4, изображений и т.д.)
|
||||
$mime_type = mime_content_type($full_path);
|
||||
|
||||
// Устанавливаем заголовки
|
||||
header('Content-Type: ' . $mime_type);
|
||||
header('Content-Length: ' . filesize($full_path));
|
||||
|
||||
// Отдаём файл
|
||||
readfile($full_path);
|
||||
exit();
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'File not found']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
header('Content-Type: application/json');
|
||||
echo gen_json((object)['msg' => "Не передан mode"]);
|
||||
break;
|
||||
}
|
||||
?>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
require_once './lib/php/DataBaseManager/DBManager.php';
|
||||
require_once './lib/php/DataBaseManager/Entitie.php';
|
||||
require_once './lib/php/Catcher/Catcher.php';
|
||||
|
||||
use DataBaseManager\Entitie\Entitie;
|
||||
use DataBaseManager\DBManager\DBManager;
|
||||
use function Catcher\recovery;
|
||||
|
||||
$config = (object) [
|
||||
'driver' => 'mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => 3306,
|
||||
'dbname' => 'anodb',
|
||||
'username' => 'root',
|
||||
'password' => 'root'
|
||||
];
|
||||
|
||||
$entitie = new Entitie([
|
||||
'name varchar(32) not null',
|
||||
'type varchar(16) not null',
|
||||
'period varchar(16) not null',
|
||||
'longitude double not null',
|
||||
'latitude double not null',
|
||||
'info_exist tinyint(1) not null',
|
||||
'slider json',
|
||||
'images json',
|
||||
'video text',
|
||||
'background text'
|
||||
], 'settlements');
|
||||
|
||||
$db_manager = new DBManager($entitie, $config);
|
||||
?>
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* @typedef {Object} MarkerData
|
||||
* @property {Blob} background - фон
|
||||
* @property {Array<Blob>} images - изображения
|
||||
* @property {Array<Blob>} slider - слайды
|
||||
*/
|
||||
|
||||
/** Путь к php-прослойке, работающая с бд и внутренним хранилищем */
|
||||
const db_get_filepath = '../db_get.php'
|
||||
/** Дефолтный срок годности кэша : 2 часа */
|
||||
const CACHE_DEFAULT_TTL_MS = 2 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Излечь данные из ответа
|
||||
* @param {string} type - тип извлекаемых данных
|
||||
* @param {Response} response - ответ
|
||||
* @returns {Promise<Object|Blob>}
|
||||
*/
|
||||
const extract_file = recovery(async (type, response) => {
|
||||
switch(type) {
|
||||
case 'file':
|
||||
return await response.blob()
|
||||
case 'json':
|
||||
return await response.json()
|
||||
default:
|
||||
throw new Error('Не определен type')
|
||||
}
|
||||
}, (...e) => {throw Error(`Ошибки при извлечении (extract_file): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* @param {string} path - путь (либо локальный или https)
|
||||
* @param {object} options - параметры fetch
|
||||
* @param {int} timeout - секундомер
|
||||
*/
|
||||
const fetch_with_timeout = recovery(async (path, options = {}, timeout = 10 * 1000) => {
|
||||
/** Штука для преждевременного завершения fetch */
|
||||
const controller = new AbortController()
|
||||
/** Как я понял, якорь для fetch */
|
||||
const signal = controller.signal
|
||||
|
||||
/** Сработает завершение после истечения timeout */
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||
/** Запрос с привязкой якоря */
|
||||
const response = await fetch(path, {...options, signal})
|
||||
|
||||
try {return response}
|
||||
finally {clearTimeout(timeoutId)}
|
||||
}, (e) => {
|
||||
if(e.name === 'AbortError')
|
||||
console.log('Запрос прерван по таймауту:', e)
|
||||
else console.log(e)
|
||||
}, (...e) => {throw Error(`Ошибки при запросе с таймером (fetch_with_timeout): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Вернуть данные из ответа, кэшируя перед этим
|
||||
* @param {string} type - тип извлекаемых данных
|
||||
* @param {string} path - путь (либо локальный или https)
|
||||
* @param {Object} body_req - тело запроса
|
||||
* @returns {Promise<Object|Blob>}
|
||||
*/
|
||||
const return_fetch = recovery(async (type, path, cache_key, body_req) => {
|
||||
const cache = await caches.open('cache')
|
||||
|
||||
/** Запрос по пути (если есть тело, то локальное обращение к файлу) */
|
||||
const response = !body_req ? await fetch_with_timeout(path) : await fetch_with_timeout(path, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(body_req)
|
||||
})
|
||||
|
||||
/** Клонирование, т.к blob/json необратимый процесс. */
|
||||
const cloned = response.clone()
|
||||
const blob = await cloned.blob()
|
||||
|
||||
const headers = new Headers(cloned.headers)
|
||||
/** Добавление даты создания в заголовок 'date' кешируемого ответа. */
|
||||
headers.set('date', new Date().toUTCString())
|
||||
|
||||
/** Процесс кэширования */
|
||||
await cache.put(
|
||||
cache_key,
|
||||
new Response(blob, {
|
||||
status: cloned.status,
|
||||
statusText: cloned.statusText,
|
||||
headers
|
||||
})
|
||||
)
|
||||
|
||||
/** Возвращаем оригинальный (первый) ответ */
|
||||
return await extract_file(type, response)
|
||||
}, (...e) => {throw Error(`Ошибки при запросе и кэшировании (return_fetch): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Получить данные из пути(локальный или https)
|
||||
* @param {*} type - тип извлекаемых данных
|
||||
* @param {*} path - путь (либо локальный или https)
|
||||
* @param {*} body_req - тело запроса
|
||||
* @returns {Promise<Object|Blob>}
|
||||
*/
|
||||
const get = recovery(async (type, path, body_req = null) => {
|
||||
const cache = await caches.open('cache')
|
||||
|
||||
/** Ключ для добавления или получения кэш-данных. */
|
||||
const cache_key = `${path}?type=${type}&file=${encodeURIComponent(JSON.stringify(body_req || {}))}`
|
||||
|
||||
/** Результат поиска ответа в кэше */
|
||||
const cached_response = await cache.match(cache_key)
|
||||
|
||||
if (cached_response) {
|
||||
/** Срок годности кэша (если задан) */
|
||||
const expires_value = cached_response.headers.get('expires')
|
||||
/** Дата создания кэша */
|
||||
const date_value = cached_response.headers.get('date')
|
||||
/** До какого числа годен */
|
||||
const exp = expires_value && expires_value !== '-1'
|
||||
? Date.parse(expires_value)
|
||||
: Date.parse(date_value ?? '') + CACHE_DEFAULT_TTL_MS
|
||||
/** Если exp больше, вернуть ответ из кэша, иначе удалить протухшие данные */
|
||||
if (Date.now() < exp) {
|
||||
return await extract_file(type, cached_response )
|
||||
}
|
||||
await cache.delete(cache_key)
|
||||
}
|
||||
/** Формирование нового кэша, т.к предыдущий протух или его нет */
|
||||
return await return_fetch(type, path, cache_key, body_req)
|
||||
}, (...e) => {throw Error(`Ошибки при получении (get): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Получить данные на диске (абсолютный/относительный путь)
|
||||
* @param {string} filepath - путь к файлу
|
||||
* @returns {Promise<Blob>} - без json
|
||||
*/
|
||||
const get_local = recovery(async (filepath) => {
|
||||
return await get('file', db_get_filepath, {
|
||||
mode: 'file',
|
||||
path: filepath
|
||||
})
|
||||
}, (...e) => {throw Error(`Ошибки при локальном получении (get_local): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Получить данные без кэширования
|
||||
* @param {string} type - тип извлекаемых данных
|
||||
* @param {string} path - путь (либо локальный или https)
|
||||
* @returns {Promise<Object|Blob>}
|
||||
*/
|
||||
const get_no_cached = recovery(async (type, path) => {
|
||||
const file = await fetch_with_timeout(path)
|
||||
return await extract_file(type, file)
|
||||
}, (...e) => {throw Error(`Ошибки при получении без кэширования (get_no_cached): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Получить метериалы поселения для инфо. страницы
|
||||
* @param {MarkerData} properties
|
||||
* - нужные свойства (пути) из свойств feature.
|
||||
* @returns {Promise<{ background: Blob, images: Blob[], slider: Blob[]}>}
|
||||
*/
|
||||
const get_for_info_page = recovery(async (properties) => {
|
||||
/** Фон инфо. страницы */
|
||||
const background = await get_local(properties.background)
|
||||
|
||||
/** Фотографи инфо. страницы */
|
||||
const images = await Promise.all(
|
||||
(properties.images || []).map(i_path => get_local(i_path))
|
||||
)
|
||||
|
||||
/** Слайды инфо. страницы (если есть) */
|
||||
const slider = Array.isArray(properties.slider)
|
||||
? await Promise.all(properties.slider.map(s_path => get_local(s_path)))
|
||||
: []
|
||||
|
||||
return {
|
||||
background,
|
||||
images,
|
||||
slider,
|
||||
}
|
||||
}, (...e) => {throw Error(`Ошибки при получении материалов для страницы (get_for_info_page): ${e.join(', ')}`)} )
|
||||
|
||||
/**
|
||||
* Получить иконки для стилизации страниц.
|
||||
* @returns {Promise<Object|Blob>}
|
||||
*/
|
||||
const get_icons = recovery(async () => await get_no_cached ('json', './data/icons.json')
|
||||
, (...e) => {throw Error(`Ошибки при получении данных для стилизации (get_icons): ${e.join(', ')}`)} )
|
||||
/**
|
||||
* Получить все geojson'ы из базы данных
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
const get_all_geojsons = recovery(async () => await get('json', db_get_filepath, {'mode': 'geojsons'})
|
||||
, (...e) => {throw Error(`Ошибки при получении geojson'ов из бд (get_all_geojsons): ${e.join(', ')}`)} )
|
||||
|
After Width: | Height: | Size: 881 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 293 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"markers": {
|
||||
"Село": "./icons/markers/Selo.png",
|
||||
"Деревня": "./icons/markers/Drevnya.png",
|
||||
"Поселок": "./icons/markers/Poselok1.png",
|
||||
"Юрта": "./icons/markers/Urta.png",
|
||||
"Археообъект": "./icons/markers/Arch.png"
|
||||
},
|
||||
"others": {
|
||||
"next": "./icons/style/next.png",
|
||||
"previous": "./icons/style/previous.png",
|
||||
"close": "./icons/style/close.svg",
|
||||
"map": "./icons/func_btn/map_btn.jpg",
|
||||
"slider": "./icons/func_btn/slider_btn.png",
|
||||
|
||||
"malaya_logo": "./icons/func_img/malaya_rodina.png",
|
||||
"rmc_logo": "./icons/func_img/rmc_logo.png"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" stroke="#ffffff">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path fill="#ffffff" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 747 B |
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 52 52" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g data-name="Group 132" id="Group_132">
|
||||
<path d="M14,52a2,2,0,0,1-1.41-3.41L35.17,26,12.59,3.41a2,2,0,0,1,0-2.82,2,2,0,0,1,2.82,0l24,24a2,2,0,0,1,0,2.82l-24,24A2,2,0,0,1,14,52Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 52 52" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g data-name="Group 132" id="Group_132">
|
||||
<path d="M38,52a2,2,0,0,1-1.41-.59l-24-24a2,2,0,0,1,0-2.82l24-24a2,2,0,0,1,2.82,0,2,2,0,0,1,0,2.82L16.83,26,39.41,48.59A2,2,0,0,1,38,52Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
|
|
@ -0,0 +1,103 @@
|
|||
<!--
|
||||
MetaInfo
|
||||
Author of the reissue: Diller(Кутман)
|
||||
Date of change: 19.05.2025
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
|
||||
<!-- Подключение OL -->
|
||||
<link rel="stylesheet" href="../lib/ol/ol.css">
|
||||
<script src="../lib/ol/ol.js"></script>
|
||||
|
||||
<!-- Подключение самописных модулей -->
|
||||
<script src="../lib/js/catcher.js"></script>
|
||||
<script src="../lib/js/page-manager.js"></script>
|
||||
<script src="../lib/js/logger.js"></script>
|
||||
<script src="../lib/js/single-layer-manager.js"></script>
|
||||
<script src="../lib/js/ref-manager.js"></script>
|
||||
<script src="../lib/js/popup-manager.js"></script>
|
||||
<script src="../lib/js/slider.js"></script>
|
||||
|
||||
<!-- Подключение основных файлов -->
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<script src="./main.js"></script>
|
||||
<script src="./data-manager.js"></script>
|
||||
|
||||
<!-- Файлы инициалиазции страниц -->
|
||||
<script src="./page/main-page.js"></script>
|
||||
<script src="./page/info-page.js"></script>
|
||||
<script src="./page/slider-page.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Область для страниц -->
|
||||
<div id="page-space">
|
||||
<!-- Главная страница -->
|
||||
<div id="main-page">
|
||||
<!-- Карта -->
|
||||
<div id="map"></div>
|
||||
|
||||
<!-- UI-компоненты -->
|
||||
<div id="main">
|
||||
<div id="left-div">
|
||||
<div class="common" id="logos"></div>
|
||||
</div>
|
||||
<div id="center-div">
|
||||
<div class="common" id="title">
|
||||
<p>Деньщиковская волость @Е-ГЕО</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="right-div">
|
||||
<div class="common" id="time-select">
|
||||
</div>
|
||||
<div class="common" id="legend"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Страница информации -->
|
||||
<div id="info-page">
|
||||
<div id="info-page-images"></div>
|
||||
<video id="info-page-video" controls="true"></video>
|
||||
<!-- Модальное окно -->
|
||||
<div id="myModal" class="modal">
|
||||
<span class="close">×</span>
|
||||
<img class="modal-content" id="modalImg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Страница с слайдером -->
|
||||
<div id="slider-page">
|
||||
<div id="slider-container">
|
||||
<div class="side" id="left">
|
||||
<img id="previous">
|
||||
</div>
|
||||
<div id="center">
|
||||
<div id="slider"></div>
|
||||
</div>
|
||||
<div class="side" id="right">
|
||||
<img id="next">
|
||||
</div>
|
||||
</div>
|
||||
<img id="to-info-page">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Служебные компоненты (не отрисовываются при загрузке) -->
|
||||
<div id="popup">
|
||||
<div id="popup-header">
|
||||
<a id="popup-title"></a>
|
||||
<img id="popup-close">
|
||||
</div>
|
||||
<div id="popup-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @typedef {import ('./lib/js-lib/page-manager.js').PageManager} PageManager
|
||||
*/
|
||||
|
||||
/**
|
||||
* Основной элемент для управления страницами
|
||||
* @type {PageManager}
|
||||
*/
|
||||
var page_manager
|
||||
|
||||
/** Основной метод */
|
||||
const main = async () => {
|
||||
page_manager = new PageManager('page-space',
|
||||
{
|
||||
element: 'main-page',
|
||||
init: main_page_init,
|
||||
},
|
||||
{
|
||||
element: 'info-page',
|
||||
init: info_page_init,
|
||||
always_mode: true
|
||||
},
|
||||
{
|
||||
element: 'slider-page',
|
||||
init: slider_page_init,
|
||||
always_mode: true
|
||||
}
|
||||
)
|
||||
|
||||
page_manager.set_page('main-page', {page_manager})
|
||||
}
|
||||
|
||||
|
||||
window.onload = main
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* @typedef {import ('/lib/ref-manager.js').RefManager} RefManager
|
||||
* @typedef {import('/lib/single-layer-manager.js').SingleLayerManager} SingleLayerManager
|
||||
* @typedef {import ('/lib/popup-manager.js').PopupManager} PopupManager
|
||||
* @typedef {import ('/lib/page-manager.js').PageManager} PageManager
|
||||
*
|
||||
* @typedef {Object} RefContent
|
||||
* @property {string} background
|
||||
* @property {Array<string>} images
|
||||
* @property {Array<string>} slider
|
||||
*
|
||||
* @typedef {Object} RefData
|
||||
* @property {RefContent} refs - ссылки
|
||||
* @property {RefManager} refs_manager - менеджер ссылок (устаревший, теперь null)
|
||||
*/
|
||||
|
||||
/** Вствка изображений в панель */
|
||||
const add_img = (div, img_ref, onclick = () => {}) => {
|
||||
/** Создание dom-изображения */
|
||||
const img = document.createElement('img')
|
||||
|
||||
/** Инъекция ссылки */
|
||||
img.src = img_ref
|
||||
|
||||
/** Инъекция поведения */
|
||||
img.onclick = onclick
|
||||
|
||||
/** Вставка */
|
||||
div.appendChild(img)
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция инициализации инфо. страницы
|
||||
* @param {PageManager} page_manager
|
||||
* @function
|
||||
*/
|
||||
const info_page_init = async ({page_manager, icon_refs_manager = null, properties}) => {
|
||||
|
||||
/**
|
||||
* Инициалиазция изображений и их помещение в колонку
|
||||
* @function
|
||||
* @param {Array<string>} images_refs - изображения маркера
|
||||
* @param {HTMLElement} modal
|
||||
* @param {HTMLElement} modal_img
|
||||
*/
|
||||
const images_init = async (images_refs, modal, modal_img) => {
|
||||
/** Панель изображений из info-page */
|
||||
const images = document.getElementById('info-page-images')
|
||||
|
||||
/** Вставка изображений в панель */
|
||||
images_refs.forEach(
|
||||
img_ref => add_img(
|
||||
images,
|
||||
img_ref,
|
||||
() => {
|
||||
modal.style.display = 'flex'
|
||||
modal_img.src = img_ref
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициалиазирует фон
|
||||
* @function
|
||||
* @param {string} background - ссылка на фон
|
||||
*/
|
||||
const background_init = (background) => {
|
||||
const info_page = document.getElementById('info-page')
|
||||
|
||||
if (typeof background === 'string') {
|
||||
// Заменяем \ на /
|
||||
background = background.replace(/\\/g, '/')
|
||||
|
||||
// Убеждаемся, что путь начинается с /
|
||||
if (!background.startsWith('/')) {
|
||||
background = '/' + background
|
||||
}
|
||||
|
||||
info_page.style.backgroundImage = `url(${background})`
|
||||
} else {
|
||||
info_page.style.backgroundImage = ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Функция очистки после инита страницы
|
||||
* @function
|
||||
*/
|
||||
const clear = () => {
|
||||
/** Очистка картинок */
|
||||
const images = document.getElementById('info-page-images')
|
||||
|
||||
/** Видео элемент */
|
||||
const video = document.getElementById('info-page-video')
|
||||
|
||||
/** Очистка внутренностей элементов */
|
||||
images.innerHTML = ''
|
||||
if (video) {
|
||||
video.pause()
|
||||
video.removeAttribute('src')
|
||||
video.load()
|
||||
video.innerHTML = ''
|
||||
video.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация модальной функциональности
|
||||
* @function
|
||||
* @returns {{modal: HTMLElement, modal_img: HTMLElement}}
|
||||
*/
|
||||
const modal_init = () => {
|
||||
const close = document.querySelector('.close')
|
||||
const modal = document.getElementById('myModal')
|
||||
const modal_img = document.getElementById('modalImg')
|
||||
|
||||
/** Условия закрытия модального окна */
|
||||
close.onclick = function () {
|
||||
modal.style.display = 'none'
|
||||
}
|
||||
|
||||
/** Тоже условие закрытия */
|
||||
window.onclick = function (event) {
|
||||
if (event.target == modal) {
|
||||
modal.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
return {modal, modal_img}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация видео
|
||||
* @function
|
||||
* @param {string} ref - прямая ссылка на видеофайл
|
||||
*/
|
||||
const video_init = async (ref) => {
|
||||
const video = document.getElementById('info-page-video')
|
||||
video.style.display = 'block'
|
||||
|
||||
// Очистка предыдущих источников
|
||||
video.innerHTML = ''
|
||||
|
||||
const source = document.createElement('source')
|
||||
source.src = ref
|
||||
source.type = 'video/mp4'
|
||||
|
||||
video.appendChild(source)
|
||||
video.load()
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание функциональных кнопок (назад, к слайдеру)
|
||||
* @function
|
||||
* @param {string} to_map - ссылка на иконку кнопки к карте (назад)
|
||||
* @param {string} to_slider - ссылка на иконку кнопки слайдер
|
||||
* @param {PageManager} page_manager - менеджер страниц
|
||||
*/
|
||||
const func_btn_init = (to_map, to_slider, page_manager, icon_refs_manager, properties) => {
|
||||
const images = document.getElementById('info-page-images')
|
||||
|
||||
if (to_slider && typeof to_slider === 'string') {
|
||||
add_img(images, to_slider, () => {
|
||||
clear()
|
||||
page_manager.set_page('slider-page', {
|
||||
page_manager,
|
||||
icon_refs_manager,
|
||||
properties
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (to_map && typeof to_map === 'string') {
|
||||
add_img(images, to_map, () => {
|
||||
clear()
|
||||
page_manager.set_page('main-page', {page_manager})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Защищенный вызов */
|
||||
recovery(
|
||||
async () => {
|
||||
// /** Получение информации маркера (ссылки напрямую) */
|
||||
// const data = await get_for_info_page(properties)
|
||||
|
||||
// /** Инициализация ref-менеджер и создание ссылок */
|
||||
// const {refs} = await refs_data_init(data)
|
||||
|
||||
/** Элементы modal */
|
||||
const {modal, modal_img} = modal_init()
|
||||
|
||||
background_init(properties.background)
|
||||
|
||||
images_init(properties.images, modal, modal_img)
|
||||
|
||||
if (properties.video && typeof properties.video === 'string') {
|
||||
video_init(properties.video)
|
||||
}
|
||||
|
||||
func_btn_init(
|
||||
icon_refs_manager.get("map"),
|
||||
properties.slider.length <= 0 ? null : icon_refs_manager.get("slider"),
|
||||
page_manager,
|
||||
icon_refs_manager,
|
||||
properties
|
||||
)
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициалиазции main-page")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
) ()
|
||||
}
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
/**
|
||||
* @typedef {import ('/lib/js/ref-manager.js').RefManager} RefManager
|
||||
* @typedef {import('/lib/js/single-layer-manager.js').SingleLayerManager} SingleLayerManager
|
||||
* @typedef {import ('/lib/js/popup-manager.js').PopupManager} PopupManager
|
||||
* @typedef {import ('/lib/js/page-manager.js').PageManager} PageManager
|
||||
*/
|
||||
|
||||
/**
|
||||
* Генерация функции для получения стилей маркера
|
||||
* @function
|
||||
* @param {Object<string, string>} marker_refs - объект с ссылками
|
||||
* @returns {((feature) => ol.style.Style)}
|
||||
*/
|
||||
const gen_get_style = recovery(
|
||||
marker_refs => feature => recovery(
|
||||
() => {
|
||||
/** Проверка feature */
|
||||
if (!feature || typeof feature.getProperties !== 'function') {
|
||||
throw new Error("Некорректный feature");
|
||||
}
|
||||
|
||||
/** Свойства feature */
|
||||
const properties = feature.getProperties()
|
||||
|
||||
/** Ссылка к иконке */
|
||||
const url = marker_refs[properties.type.toLowerCase()]
|
||||
?? marker_refs['археообъект'.toLowerCase()]
|
||||
|
||||
/** Генерация стилей */
|
||||
return new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 1],
|
||||
scale: 0.08,
|
||||
src: url
|
||||
})
|
||||
})
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при получении стилей (gen_get_style->get_style)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)(),
|
||||
(...e) => {
|
||||
console.log("Ошибка генерации функции стилей (gen_get_style)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Функция инициализирует карту
|
||||
* @function
|
||||
* @param {{coordinates: [number, number], target?: string}} arg
|
||||
* @returns {{map: ol.Map, layer: ol.layer.Vector}}
|
||||
*/
|
||||
const create_map_and_layer = ({coordinates, target = "map"}, style_f) => {
|
||||
/** Одиночный слой */
|
||||
let layer = new ol.layer.VectorImage({
|
||||
source: new ol.source.Vector({}),
|
||||
style: (feature) => style_f(feature)
|
||||
})
|
||||
|
||||
/** Создание карты и её привязка к dom-элементу */
|
||||
const map = new ol.Map({
|
||||
view: new ol.View({
|
||||
center: ol.proj.fromLonLat(coordinates),
|
||||
zoom: 10,
|
||||
minZoom: 8
|
||||
}),
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.OSM(),
|
||||
}),
|
||||
layer
|
||||
],
|
||||
target: target
|
||||
})
|
||||
|
||||
return {map, layer}
|
||||
}
|
||||
|
||||
/**
|
||||
* Активация стилей кнопки
|
||||
* @function
|
||||
*/
|
||||
const active = (btn) => btn.classList.replace('not-selected', 'selected')
|
||||
/**
|
||||
* Деактивация стилей кнопки
|
||||
* @function
|
||||
*/
|
||||
const deactive = (btn) => btn.classList.replace('selected', 'not-selected')
|
||||
|
||||
/**
|
||||
* Функция инициализации главой страницы
|
||||
* @function
|
||||
* @param {PageManager} page_manager
|
||||
* @param {RefManager} [previous_refs_manager] - менеджер ссылок предыдущей страницы
|
||||
*/
|
||||
const main_page_init = async ({page_manager}) => {
|
||||
/**
|
||||
* Cоздание ссылок из загруженных фотографий
|
||||
* @function
|
||||
* @returns {RefManager}
|
||||
*/
|
||||
const icon_refs_manager_init = async () => {
|
||||
/** Данные (иконки) из внешнего json */
|
||||
const icons_data = (await get('json', './icons/icons.json'))
|
||||
const now_icons_data = {...icons_data.markers, ...icons_data.others}
|
||||
|
||||
/** Менеджер иконок, заполненный ссылками */
|
||||
let icon_ref_manager = new RefManager()
|
||||
for (const [name, path] of Object.entries(now_icons_data))
|
||||
icon_ref_manager.save({
|
||||
key: name.toLowerCase(),
|
||||
ref: URL.createObjectURL(await get('file', path))
|
||||
})
|
||||
|
||||
return icon_ref_manager
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание и ининциализация карты
|
||||
* @function
|
||||
* @param {RefManager} icon_ref_manager - менеджер ссылок
|
||||
* @returns {{map: ol.Map, layer: ol.layer.Vector}}
|
||||
*/
|
||||
const map_init = (icon_refs_manager) =>
|
||||
create_map_and_layer(
|
||||
{
|
||||
coordinates: [70.008408, 60.001500],
|
||||
target: "map"
|
||||
},
|
||||
gen_get_style(icon_refs_manager.get_all())
|
||||
)
|
||||
|
||||
/**
|
||||
* Создание и заполнение элемента с кнопками периодов
|
||||
* @function
|
||||
* @param {Object} geojsons - объект, содержащий все geojson'ы
|
||||
*/
|
||||
const periods_init = (geojsons, single_layer_manager) => {
|
||||
/** DOM-элемент, куда кнопки периодов загружаются */
|
||||
const time_select = document.getElementById('time-select')
|
||||
const periods = Object.keys(geojsons)
|
||||
.sort((a, b) => parseInt(a) - parseInt(b))
|
||||
|
||||
/** Массив кнопок с период-кнопками */
|
||||
const buttons_periods = periods.map(period => {
|
||||
/** Период-кнопка */
|
||||
const button = document.createElement('button')
|
||||
button.dataset.period = period;
|
||||
button.textContent = period;
|
||||
|
||||
/** Добавление стилей: неактивная кнопка */
|
||||
button.classList.add('not-selected')
|
||||
|
||||
/** Добавление в панель */
|
||||
time_select.appendChild(button)
|
||||
return button
|
||||
})
|
||||
|
||||
/** Добавление слушателей (Загрузка geojson + сохранение состояния) */
|
||||
buttons_periods.forEach(bp => {
|
||||
bp.addEventListener('click', async () => {
|
||||
/** Изменение слоя на карте */
|
||||
single_layer_manager.set(geojsons[bp.dataset.period])
|
||||
|
||||
/** Сохранение состояния */
|
||||
localStorage.setItem('selected', bp.dataset.period);
|
||||
|
||||
/** Выключение всех остальных кнопок */
|
||||
buttons_periods.forEach(deactive)
|
||||
|
||||
/** Включение текущей кнопки */
|
||||
active(bp)
|
||||
})
|
||||
})
|
||||
|
||||
/** Загрузка последнего сохраненного статуса-периода (Имитация выбора) */
|
||||
const selected = localStorage.getItem('selected')
|
||||
const button_select = buttons_periods.find(bp => bp.dataset.period === selected);
|
||||
if (button_select) {
|
||||
button_select.click()
|
||||
} else
|
||||
buttons_periods[0].click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание и заполнение легенды (маркер - название)
|
||||
* @function
|
||||
* @param {RefManager} icon_refs_manager - менеджер ссылок
|
||||
*/
|
||||
const legend_init = (icon_refs_manager) => {
|
||||
/** DOM-элемент легенды */
|
||||
const legend = document.getElementById('legend')
|
||||
|
||||
for(const [name, ref] of Object.entries(icon_refs_manager.get_all())) {
|
||||
/** Row в легенде */
|
||||
const div = document.createElement('div')
|
||||
|
||||
/** Иконка - маркер */
|
||||
const marker = document.createElement('img')
|
||||
|
||||
/** Текст - название */
|
||||
const p = document.createElement('p');
|
||||
|
||||
/** Помещение данных в row */
|
||||
marker.src = ref
|
||||
p.innerHTML = name
|
||||
|
||||
/** Добавление в контейенер легенды */
|
||||
div.appendChild(marker)
|
||||
div.appendChild(p)
|
||||
legend.appendChild(div)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание логотипов с переходом на другие страницы
|
||||
* @function
|
||||
* @param {RefManager} icon_refs_manager - менеджер ссылок
|
||||
*/
|
||||
const logos_init = (icon_refs_manager) => {
|
||||
/** DOM-элемент для логотипов */
|
||||
const logos = document.getElementById('logos')
|
||||
|
||||
/**
|
||||
* Линковка ссылок на изображения и onclick'ов
|
||||
* @param {string} img_src
|
||||
* @param {(() => {})} onclick
|
||||
* @returns
|
||||
*/
|
||||
const logo_init = (img_src, onclick) => {
|
||||
/** Создание dom-элемента */
|
||||
const logo = document.createElement('img')
|
||||
/** Инъекция аргументов */
|
||||
logo.src = img_src
|
||||
logo.onclick = onclick
|
||||
|
||||
return logo
|
||||
}
|
||||
|
||||
/** Создание и добавление логотипов */
|
||||
[
|
||||
logo_init(
|
||||
icon_refs_manager.get('malaya_logo'),
|
||||
() => window.location.href = 'https://vk.com/anomalaya_rodina'
|
||||
),
|
||||
logo_init(
|
||||
icon_refs_manager.get('rmc_logo'),
|
||||
() => window.location.href = 'https://vk.com/rcod_hmao'
|
||||
)
|
||||
].forEach(logo => logos.appendChild(logo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация popup-manager
|
||||
* @function
|
||||
* @param {ol.Map} map - ol-карта
|
||||
* @param {PageManager} page_manager - менеджер страниц
|
||||
* @param {RefManager} refs_manager - менеджер ссылок
|
||||
* @param {string} close_icon - икнонка крестика
|
||||
* @returns {PopupManager}
|
||||
*/
|
||||
const popup_manager_init = (map, page_manager, refs_manager, close_icon) => {
|
||||
/** Смещение popup */
|
||||
const offset = [0, 10]
|
||||
|
||||
/** Контейнер popup */
|
||||
const popup = document.getElementById('popup')
|
||||
|
||||
/** Закрывашка, привязка ссылки иконки*/
|
||||
const popup_close = document.getElementById('popup-close')
|
||||
popup_close.src = close_icon
|
||||
|
||||
/** Заголовок всплывающего окна */
|
||||
const popup_title = document.getElementById('popup-title')
|
||||
|
||||
/** Контент всплывающего окна */
|
||||
const popup_content = document.getElementById('popup-content')
|
||||
|
||||
/**
|
||||
* Поведение карты при нажатии на карту
|
||||
* @param {ol.Feature} feature - объект маркера
|
||||
*/
|
||||
const map_on = feature => {
|
||||
/** Формирование контента */
|
||||
popup_title.innerHTML = feature.getProperties().name
|
||||
popup_content.innerHTML = ''
|
||||
|
||||
/** Формирование кнопки 'подробнее' и его привязка */
|
||||
if (feature.getProperties().info_exist) {
|
||||
/** Создание ссылки 'подробнее' */
|
||||
const content_description = document.createElement('a')
|
||||
content_description.innerHTML = 'подробнее'
|
||||
content_description.href = '#'
|
||||
|
||||
/** Привязка */
|
||||
popup_content.appendChild(content_description)
|
||||
|
||||
/** Переход к info-page */
|
||||
content_description.onclick = () =>
|
||||
page_manager.set_page('info-page', {
|
||||
page_manager: page_manager,
|
||||
icon_refs_manager: refs_manager,
|
||||
properties: feature.getProperties()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return new PopupManager({
|
||||
map: map,
|
||||
popup_div: popup,
|
||||
popup_close: popup_close,
|
||||
offset: offset,
|
||||
map_on: map_on
|
||||
})
|
||||
}
|
||||
|
||||
/** Защищенный вызов */
|
||||
await recovery(
|
||||
async () => {
|
||||
/** Получение всех иконок */
|
||||
const icon_ref_manager = await icon_refs_manager_init()
|
||||
|
||||
/** Инициализация */
|
||||
const {map, layer} = map_init(icon_ref_manager)
|
||||
const single_layer_manager = new SingleLayerManager(layer)
|
||||
|
||||
/** Получение всех geojson'ов */
|
||||
const geojsons = await get_all_geojsons()
|
||||
|
||||
periods_init(geojsons, single_layer_manager)
|
||||
|
||||
legend_init(icon_ref_manager)
|
||||
logos_init(icon_ref_manager)
|
||||
|
||||
popup_manager_init(map, page_manager, icon_ref_manager, icon_ref_manager.get('close'))
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициалиазции main-page");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)()
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* @typedef {import('../lib/js-lib/slider.js').Slider} Slider
|
||||
* @typedef {import('../lib/js-lib/page-manager.js').PageManager} PageManager
|
||||
* @typedef {import('../lib/js-lib/ref-manager.js').RefManager} RefManager
|
||||
*
|
||||
* @typedef {Object} SliderPageArg
|
||||
* @property {RefManager} slider_urls_manager - менеджер для слайдера
|
||||
* @property {RefManager} icon_refs_manager - менеджер иконок
|
||||
* @property {PageManager} page_manager - менеджер страниц
|
||||
* @property {Object} properties - свойства маркера
|
||||
*/
|
||||
|
||||
/**
|
||||
* Инициализация slider-page
|
||||
* @function
|
||||
* @param {SliderPageArg} arg
|
||||
*/
|
||||
const slider_page_init = ({page_manager, icon_refs_manager, properties}) => {
|
||||
|
||||
/**
|
||||
* Инициализация кнопки возврата на info_page
|
||||
* @function
|
||||
*/
|
||||
const to_info_page_init = (slider) => {
|
||||
const to_info_page = document.getElementById('to-info-page');
|
||||
|
||||
// Иконка кнопки
|
||||
to_info_page.src = icon_refs_manager.get("map");
|
||||
|
||||
to_info_page.onclick = () => {
|
||||
// Очистка
|
||||
slider.clear?.();
|
||||
|
||||
page_manager.set_page('info-page', {
|
||||
page_manager,
|
||||
icon_refs_manager,
|
||||
properties
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/** Безопасный вызов */
|
||||
recovery(
|
||||
() => {
|
||||
// Получение изображений из менеджера
|
||||
const images = properties.slider
|
||||
|
||||
// Создание слайдера
|
||||
const slider = new Slider({
|
||||
slider: {
|
||||
main: document.getElementById('slider'),
|
||||
next: document.getElementById('next'),
|
||||
previous: document.getElementById('previous')
|
||||
},
|
||||
next_ref: icon_refs_manager.get('next'),
|
||||
previous_ref: icon_refs_manager.get('previous'),
|
||||
images
|
||||
});
|
||||
|
||||
// Кнопка назад к info_page
|
||||
to_info_page_init(slider);
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициализации (slider-page)");
|
||||
e.forEach(er => { throw er });
|
||||
}
|
||||
)();
|
||||
};
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/** MetaInfo
|
||||
* Author of the reissue: Diller(Кутман)
|
||||
* Date of change: 19.05.2025
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #222;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 35px;
|
||||
color: #fff;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#page-space {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#main-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#info-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#info-page-images {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 150px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#info-page-images img {
|
||||
object-fit: cover;
|
||||
background-position: center;
|
||||
margin: 10px 10px 0px 10px;
|
||||
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
#info-page-video {
|
||||
display: none;
|
||||
position: fixed;
|
||||
height: 250px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
#map {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#left-div {
|
||||
display: flex;
|
||||
width: 20vw;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#logos {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
|
||||
width: 75px;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
#logos img {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
#center-div {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
#title {
|
||||
display: flex;
|
||||
z-index: 2;
|
||||
|
||||
width: 600px;
|
||||
height: 80px;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#title p {
|
||||
margin: 0;
|
||||
/* color: aliceblue */
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
#right-div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: end;
|
||||
|
||||
width: 20vw;
|
||||
|
||||
}
|
||||
|
||||
#time-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
z-index: 2;
|
||||
background-color: rgba(99, 99, 99, 0);
|
||||
|
||||
width: 150px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#time-select button {
|
||||
background-color: rgba(99, 99, 99, 0.8);
|
||||
border: 0;
|
||||
height: 10%;
|
||||
}
|
||||
|
||||
#time-select button:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
#time-select button.selected {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
#legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 2;
|
||||
|
||||
margin-bottom: 20px;
|
||||
width: 170px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
#legend div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#legend div p {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 13px;
|
||||
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
|
||||
#legend div img {
|
||||
height: 26px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.common {
|
||||
background-color: rgba(99, 99, 99, 0.8);
|
||||
border-radius: 10px;
|
||||
opacity: 0.9;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Main End */
|
||||
|
||||
#popup {
|
||||
flex-direction: column;
|
||||
|
||||
background-color: rgba(6, 6, 6, 0.8);
|
||||
border-radius: 10px;
|
||||
z-index: 2;
|
||||
|
||||
width: 220px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#popup-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#popup-title {
|
||||
display: flex;
|
||||
margin: 10px 0px 0px 10px;
|
||||
justify-content: center;
|
||||
color: aliceblue;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#popup-close {
|
||||
position: relative;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
right: 10px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#popup-content {
|
||||
margin: 10px;
|
||||
color: aliceblue;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
/* Slider Start */
|
||||
|
||||
#slider-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 10%;
|
||||
min-width: 150px;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.side img {
|
||||
height: 65px;
|
||||
}
|
||||
|
||||
#center {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#slider {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.slide {
|
||||
height: 100%;
|
||||
width: 80vw;
|
||||
flex-shrink: 0;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#to-info-page {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
|
||||
height: 100px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* Slider End */
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_manager.php';
|
||||
|
||||
use function Catcher\recovery;
|
||||
|
||||
$period_view = (object) [
|
||||
'rArcheology' => '16 век и ранее',
|
||||
'r1675' => '17 век',
|
||||
'r1740' => '18 век',
|
||||
'r1781' => '19 век',
|
||||
'r1858' => '1901-1920',
|
||||
'r1900' => '1921-1940',
|
||||
'r1926' => '1941-1960',
|
||||
'r1936' => '1961-1980',
|
||||
'r1946' => '1981-2000',
|
||||
'r200-now' => '2000 и н.в.'
|
||||
];
|
||||
|
||||
$get_fs = null;
|
||||
$get_fs = recovery(function (string $path) use (&$get_fs) {
|
||||
$result = (object) ['files' => (object)[]];
|
||||
|
||||
$dirs = array_filter(scandir($path), function ($dir) {
|
||||
return $dir !== '.' && $dir !== '..';
|
||||
});
|
||||
|
||||
foreach($dirs as $dir) {
|
||||
$new_path = $path . DIRECTORY_SEPARATOR . $dir;
|
||||
if(is_dir($new_path))
|
||||
$result->$dir = $get_fs($new_path);
|
||||
else if(is_file($new_path)) {
|
||||
$filename = pathinfo($new_path, PATHINFO_FILENAME);
|
||||
$result->files->$filename = $new_path;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}, function($e) {
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$main = recovery(function (object $period_view) use (&$get_fs,&$db_manager)
|
||||
{
|
||||
$db_manager->delete_table();
|
||||
$db_manager->create_table();
|
||||
|
||||
$fs = $get_fs('.' . DIRECTORY_SEPARATOR . 'data');
|
||||
|
||||
$removeLeadingDot = function(?string $path): ?string {
|
||||
if ($path === null) return null;
|
||||
return preg_replace('#^\.(?=[/\\\\])#', '', $path);
|
||||
};
|
||||
|
||||
|
||||
$removeLeadingDotFromJsonArray = function(?string $json): ?string {
|
||||
if ($json === null) return null;
|
||||
$arr = json_decode($json);
|
||||
if (!is_array($arr)) return $json;
|
||||
$arr = array_map(fn($p) => preg_replace('#^\.(?=.*)#', '', $p), $arr);
|
||||
return json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
};
|
||||
|
||||
|
||||
$settlements_data = [];
|
||||
|
||||
$material_path = $fs->material;
|
||||
foreach ($fs->geojson->files as $geojson_path) {
|
||||
if (!file_exists($geojson_path)) {
|
||||
echo 'Невалидный geojson-path : ' . $geojson_path;
|
||||
continue;
|
||||
}
|
||||
|
||||
$geojson = json_decode(file_get_contents($geojson_path));
|
||||
$features = $geojson->features;
|
||||
$basename = pathinfo($geojson_path, PATHINFO_FILENAME);
|
||||
$period = $period_view->$basename;
|
||||
|
||||
foreach($features as $feature) {
|
||||
$properties = $feature->properties;
|
||||
$coordinates = $feature->geometry->coordinates;
|
||||
|
||||
$en = $properties->en;
|
||||
|
||||
$info_exist = 0;
|
||||
|
||||
$background = null;
|
||||
$images = null;
|
||||
$slider = null;
|
||||
$video = null;
|
||||
|
||||
if(!empty($en) && isset($material_path->$en)) {
|
||||
$material = $material_path->$en;
|
||||
|
||||
$tmp = array_values((array)$material->background->files);
|
||||
$background = end($tmp);
|
||||
|
||||
if (!empty($material->image) && isset($material->image)) {
|
||||
$tmp = array_values((array)$material->image->files);
|
||||
$images = json_encode($tmp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: null;
|
||||
}
|
||||
|
||||
if (!empty($material->slider) && isset($material->slider)) {
|
||||
$tmp = array_values((array)$material->slider->files);
|
||||
$slider = json_encode($tmp, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: null;
|
||||
}
|
||||
|
||||
if (!empty($material->video) && isset($material->video)) {
|
||||
$tmp = array_values((array)$material->video->files);
|
||||
$video = end($tmp);
|
||||
}
|
||||
|
||||
$info_exist = 1;
|
||||
}
|
||||
|
||||
switch (mb_strtolower($properties->Tupe)) {
|
||||
case 'археология':
|
||||
$type = 'археообъект';
|
||||
break;
|
||||
case 'юрты':
|
||||
$type = 'юрта';
|
||||
break;
|
||||
default:
|
||||
$type = $properties->Tupe;
|
||||
}
|
||||
|
||||
// Убираем точку в путях перед записью в массив
|
||||
$background = $removeLeadingDot($background);
|
||||
$images = $removeLeadingDotFromJsonArray($images);
|
||||
$slider = $removeLeadingDotFromJsonArray($slider);
|
||||
$video = $removeLeadingDot($video);
|
||||
|
||||
array_push($settlements_data, [
|
||||
'name' => $properties->Name,
|
||||
'type' => $type,
|
||||
'period' => $period,
|
||||
'longitude' => $coordinates[0],
|
||||
'latitude' => $coordinates[1],
|
||||
'info_exist' => $info_exist,
|
||||
'slider' => $slider,
|
||||
'images' => $images,
|
||||
'video' => $video,
|
||||
'background' => $background
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$settlements_arg = array_map(
|
||||
fn($settlements) => array_values((array) $settlements),
|
||||
$settlements_data
|
||||
);
|
||||
|
||||
$db_manager->create_all($settlements_arg);
|
||||
}, function($e) {
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$main($period_view);
|
||||
?>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/** тип async function */
|
||||
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor
|
||||
|
||||
/**
|
||||
* Безопасный запуск востановителя
|
||||
* @param {(...errs: Error[]) => void} restorer
|
||||
*/
|
||||
const start_restorer = (restorer) => (...errs) => {
|
||||
try {
|
||||
return restorer(...errs)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция отлова исключений
|
||||
* @param {(...args: any[]) => any} func
|
||||
* @param {(...errs: Error[]) => void} restorer
|
||||
* @returns {(...args: any[]) => any}
|
||||
*/
|
||||
const recovery = (func, restorer = (...args) => {throw new Error(...args)}) => {
|
||||
let resFunc
|
||||
if(func instanceof AsyncFunction)
|
||||
resFunc = async (...args) => {
|
||||
try {return await func(...args)}
|
||||
catch (e) {return start_restorer(restorer)(e)}
|
||||
}
|
||||
else
|
||||
resFunc = (...args) => {
|
||||
try {return func(...args)}
|
||||
catch (e) {return start_restorer(restorer)(e)}
|
||||
}
|
||||
return resFunc
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
const getCurrentTime = () => {
|
||||
const date = new Date()
|
||||
return '' +
|
||||
String(date.getFullYear()) + '.' +
|
||||
String(date.getMonth()+1).padStart(2, '0') + '.' +
|
||||
String(date.getDate()).padStart(2, '0') + ' ' +
|
||||
String(date.getHours() + 5).padStart(2, '0') + ':' +
|
||||
String(date.getMinutes()).padStart(2, '0') + ':' +
|
||||
String(date.getSeconds()).padStart(2, '0')
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Logger
|
||||
* @property {(msg: string) => void} print
|
||||
* @property {(wmsg: string) => void} wprint
|
||||
* @property {(emsg: string) => void} eprint
|
||||
* @property {(msg: string) => void} save
|
||||
*/
|
||||
|
||||
/** @type {Logger} */
|
||||
const logger = {
|
||||
saved: [],
|
||||
errors: [],
|
||||
warnings: [],
|
||||
logs: [],
|
||||
/** @param {string} msg */
|
||||
print: (msg) => {
|
||||
const msg_format = `${getCurrentTime()} : Log : ${msg}`
|
||||
logger.logs.push(msg_format);
|
||||
console.log(msg_format)
|
||||
},
|
||||
/** @param {string} wmsg */
|
||||
wprint: (wmsg) => {
|
||||
const msg_format = `${getCurrentTime()} : Warning : ${wmsg}`
|
||||
logger.warnings.push(msg_format);
|
||||
console.log(msg_format)
|
||||
},
|
||||
/** @param {string} emsg */
|
||||
eprint: (emsg) => {
|
||||
const msg_format = `${getCurrentTime()} : Error : ${emsg}`
|
||||
logger.errors.push(msg_format);
|
||||
console.log(msg_format)
|
||||
},
|
||||
/** @param {string} msg */
|
||||
save: (msg) => logger.saved.push(`${getCurrentTime()} : Data : ${msg}`),
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* @typedef {Object} Page
|
||||
* @property {HTMLElement} element
|
||||
* @property {(...args: any[]) => void} init
|
||||
* @property {boolean} always_mode
|
||||
* @property {boolean} [first_init]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string | HTMLElement} element
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
const get_element = recovery((element) => {
|
||||
if(element instanceof HTMLElement)
|
||||
return element
|
||||
else if(typeof element === 'string')
|
||||
return document.getElementById(element)
|
||||
else
|
||||
throw new Error(`element is not correct: ${element}`)
|
||||
})
|
||||
|
||||
/**
|
||||
* @class PageManager
|
||||
* @property {Object<string, Page>} pages
|
||||
* @property {HTMLElement} page_space
|
||||
*/
|
||||
class PageManager {
|
||||
|
||||
/**
|
||||
* @param {string | HTMLElement} page_space_id
|
||||
* @param {...Page} args
|
||||
*/
|
||||
constructor(page_space_id, ...args) {
|
||||
recovery((page_space_id_f, ...args_f) => {
|
||||
this.pages = {}
|
||||
this.page_space = get_element(page_space_id_f)
|
||||
let first_id
|
||||
|
||||
args_f.forEach((arg, index) => {
|
||||
const {element, init = () => {}, always_mode = false} = arg
|
||||
const el = get_element(element)
|
||||
if(el instanceof HTMLElement){
|
||||
const id = el.id?.trim() || `_page_${index}`
|
||||
this.pages[id] = {element: el, init, always_mode}
|
||||
if(first_id == null) first_id = id
|
||||
} else
|
||||
logger.wprint(`${element} is not html-element or id`)
|
||||
})
|
||||
}, logger.eprint) (page_space_id, ...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} page_id
|
||||
* @param {...*} args
|
||||
*/
|
||||
set_page = recovery((page_id, ...args) => {
|
||||
const page = this.pages[page_id]
|
||||
|
||||
this.page_space.innerHTML = ''
|
||||
this.page_space.appendChild(page.element)
|
||||
|
||||
if(!page.first_init || page.always_mode) {
|
||||
page.init(...args)
|
||||
if(!page.first_init) page.first_init = true
|
||||
}
|
||||
}, logger.eprint)
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* @typedef {Object} PopupManagerArg
|
||||
* @property {ol.Map} map - ol-карта
|
||||
* @property {HTMLElement} popup_div - контейнер всплывающего окна
|
||||
* @property {HTMLElement} [popup_close] - закрывашка
|
||||
* @property {(feature: ol.Feature) => void} map_on - поведение при нажатии на map
|
||||
* @property {[number, number]} offset - смещение popup
|
||||
*/
|
||||
|
||||
/**
|
||||
* Управление и создание ol-popup
|
||||
*
|
||||
* @class PopupManager
|
||||
* @property {ol.Map} map - ol-карта
|
||||
* @property {HTMLElement} popup_div - контейнер всплывающего окна
|
||||
* @property {HTMLElement} [popup_close] - закрывашка
|
||||
* @property {(feature: ol.Feature) => void} map_on - поведение при нажатии на map
|
||||
*/
|
||||
class PopupManager {
|
||||
/**
|
||||
* @param {PopupManagerArg} args
|
||||
*/
|
||||
constructor ({map, popup_div, popup_close, map_on = (feature) => {}, offset = [0, 0]}) {
|
||||
/** Безопасный вызов */
|
||||
recovery(
|
||||
/**
|
||||
* @function
|
||||
* @param {PopupManager} self
|
||||
*/
|
||||
self => {
|
||||
/** Инъекция карты */
|
||||
if(map && map instanceof ol.Map) self.map = map
|
||||
else throw new Error("Аргумент 'map' невалиден")
|
||||
|
||||
/** Инъекция поведения при нажатии на map */
|
||||
this.set_map_on(map_on)
|
||||
|
||||
/** Инъекция popup-элемента */
|
||||
if(popup_div && popup_div instanceof HTMLElement)
|
||||
self.popup_div = popup_div
|
||||
else throw new Error("Аргумент 'popup_div' невалиден")
|
||||
|
||||
/** Инициализация popup-элемента */
|
||||
self.overlay = new ol.Overlay({
|
||||
element: self.popup_div,
|
||||
autoPan: true,
|
||||
offset: offset
|
||||
})
|
||||
|
||||
/** Регристрация overlay */
|
||||
self.map.addOverlay(self.overlay)
|
||||
|
||||
/** Добавление поведения при нажатии на карту */
|
||||
self.map.on('click', evt => {
|
||||
/** Получение данных маркера */
|
||||
const feature = self.map.forEachFeatureAtPixel(
|
||||
evt.pixel,
|
||||
(feature, _) => feature,
|
||||
{ hitTolerance: 8 }
|
||||
)
|
||||
if(feature) {
|
||||
/** Вызов переданной функции */
|
||||
self.map_on(feature)
|
||||
|
||||
/** Прилинковка popup */
|
||||
self.set_popup(feature.getGeometry().getCoordinates())
|
||||
} else {
|
||||
/** Олинковка popup */
|
||||
self.set_popup()
|
||||
}
|
||||
})
|
||||
|
||||
/** При нажатии не на маркеры убрать popup */
|
||||
document.addEventListener('click', event => {
|
||||
if (!event.target.closest('.ol-viewport') && !event.target.closest('.popup')) {
|
||||
self.set_popup()
|
||||
}
|
||||
})
|
||||
|
||||
/** Инъекция и инициалиазция закрывашки (если есть) */
|
||||
if(popup_close) {
|
||||
/** Инъекция закрывашки */
|
||||
if (popup_close instanceof HTMLElement) self.popup_close = popup_close
|
||||
else throw new Error("Аргумент 'popup_close' невалиден")
|
||||
|
||||
/** Инициаилазиця закрывашки */
|
||||
self.popup_close.onclick = function () {
|
||||
/** Отлинковка от координат */
|
||||
self.set_popup()
|
||||
/** Скрытие */
|
||||
popup_close.blur()
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициалиазции PopupManager");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
) (this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {[number, number] | undefined} [coord] - координаты привязки
|
||||
*/
|
||||
set_popup = recovery(
|
||||
(coord = undefined) => this.overlay.setPosition(coord),
|
||||
(...e) => {
|
||||
console.log("Ошибка скрытии popup (PopupManager.set_popup)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Изменение поведения при нажатии на map
|
||||
* @function
|
||||
* @param {(feature: Object) => void} map_on - поведение при нажатии на map
|
||||
*/
|
||||
set_map_on = recovery(
|
||||
map_on => this.map_on = map_on,
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициалиазции PopupManager.set_map_on");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* Удобное хранение и освобождение временных ссылок
|
||||
* ВНИМАНИЕ: При очистке освобождаются все ссылки
|
||||
*
|
||||
* @class RefManager
|
||||
* @property {Map<string, string>} refs - хранилище
|
||||
*/
|
||||
class RefManager {
|
||||
/**
|
||||
* @param {Map<string, string> | Object} [refs]
|
||||
*/
|
||||
constructor (refs = null) {
|
||||
recovery((self) => {
|
||||
/** Инициализация хранилища */
|
||||
self.refs = new Map()
|
||||
|
||||
if(refs) {
|
||||
if(Array.isArray(refs))
|
||||
refs.forEach(ref => { self.save({ref}) });
|
||||
else {
|
||||
/** Связки из аргумента refs */
|
||||
const refs_entries = (refs instanceof Map)
|
||||
? refs.entries()
|
||||
: (typeof refs === 'object' && refs !== null)
|
||||
? Object.entries(refs)
|
||||
: (() => {throw new Error("Невалидный аргумент")})()
|
||||
|
||||
/** Сохранение ссылок в хранилище */
|
||||
Array.from(refs_entries).forEach(
|
||||
([key, ref]) => self.save({key, ref})
|
||||
)
|
||||
}
|
||||
}
|
||||
}, (...e) => {
|
||||
console.log("Ошибка инициализии RefManager");
|
||||
e.forEach(er => {throw er});
|
||||
}) (this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка ссылки
|
||||
* @static @method
|
||||
* @param {string} ref - ссылка
|
||||
* @returns {string}
|
||||
*/
|
||||
static check = recovery(ref => {
|
||||
if(typeof ref !== 'string')
|
||||
throw Error("Неизвестный объект в массиве строк")
|
||||
return ref
|
||||
}, (...e) => {
|
||||
console.log("Ошибка при проверке значения (RefManager.check)");
|
||||
e.forEach(er => {throw er});
|
||||
})
|
||||
|
||||
/**
|
||||
* Сохранение сслыки
|
||||
* @method
|
||||
* @param {string} [key] - ключ
|
||||
* @param {string} ref - ссылка
|
||||
* @returns {{key: string, ref: string}}
|
||||
*/
|
||||
save = recovery(
|
||||
({key = null, ref}) => {
|
||||
var now_key = key
|
||||
? key
|
||||
: (Date.now() + Math.random()).toString(36)
|
||||
|
||||
this.refs.set(RefManager.check(now_key), RefManager.check(ref))
|
||||
return {key: now_key, ref}
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при сохранении (save)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Получение ссылки по ключу
|
||||
* @method
|
||||
* @param {string} key - ключ
|
||||
* @returns {string}
|
||||
*/
|
||||
get = recovery(
|
||||
key => {
|
||||
return this.refs.get(RefManager.check(key))
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при извлечении ссылки (get)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Очищает все сохранённые ссылки и освобождает ресурсы
|
||||
* @method
|
||||
*/
|
||||
clear = recovery(
|
||||
() => {
|
||||
Array.from(this.refs.values()).forEach(URL.revokeObjectURL);
|
||||
this.refs.clear()
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при очищении (clear)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Возвращает сконвертированную в объект список
|
||||
* @method
|
||||
* @returns {Object}
|
||||
*/
|
||||
get_all = recovery(
|
||||
() => {
|
||||
return Object.fromEntries(this.refs)
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при конвертации и получени (get_all)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* @typedef {import('./ref-manager.js').RefManager} RefManager
|
||||
*/
|
||||
|
||||
/**
|
||||
* Контроль за одним интерактивным целевым слоем.
|
||||
*
|
||||
* @class SingleLayerManager
|
||||
* @property {ol.layer.Vector} layer - тот самый single слой
|
||||
*/
|
||||
class SingleLayerManager {
|
||||
/**
|
||||
* @param {ol.layer.Vector } layer - ol-слой
|
||||
*/
|
||||
constructor (layer) {
|
||||
recovery(self => {
|
||||
/** Инъекция слоя */
|
||||
if(layer instanceof ol.layer.Vector || layer instanceof ol.layer.VectorImage)
|
||||
self.layer = layer
|
||||
else
|
||||
throw new Error("Несоответствующий тип у слоя-аргумента")
|
||||
}, (...e) => {
|
||||
console.log("Ошибка инициализии SingleLayerManager");
|
||||
e.forEach(er => {throw er});
|
||||
})(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Меняет источник целевого слоя.
|
||||
* @method
|
||||
* @param {object} json - необработанный json
|
||||
*/
|
||||
set = recovery(
|
||||
json => {
|
||||
const features = new ol.format.GeoJSON().readFeatures(json, {
|
||||
featureProjection: 'EPSG:3857'
|
||||
})
|
||||
this.layer.getSource().clear()
|
||||
this.layer.getSource().addFeatures(features)
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка изменения слоя (singleLayerManager.set)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Очистка и удаление самого менеджера
|
||||
* @method
|
||||
*/
|
||||
exit = recovery(
|
||||
() => {
|
||||
this.layer = null
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка изменения слоя (singleLayerManager.set)");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* @typedef {Object} SliderArg
|
||||
* @property {string} next_ref - сслыка на иконку вперед
|
||||
* @property {string} previous_ref - ссылка на иконку назадъ
|
||||
* @property {{main: HTMLStyleElement, next: HTMLElement, previous: HTMLElement}} slider - разграниченный слайдер
|
||||
* @property {Array<string>} images - массив ссылок
|
||||
*/
|
||||
|
||||
/**
|
||||
* Класс для создания слайдера
|
||||
* @class
|
||||
* @property {HTMLElement} main - DOM-элемент для слайдов
|
||||
* @property {number} current_slide - текущий слайд
|
||||
* @property {Array<HTMLElement>} slides - слайды
|
||||
*/
|
||||
class Slider {
|
||||
/**
|
||||
* @param {SliderArg} arg
|
||||
*/
|
||||
constructor ({slider: {main, next, previous}, next_ref, previous_ref, images = null}) {
|
||||
/** Безопасный вызов */
|
||||
recovery(
|
||||
self => {
|
||||
/** Инъекция DOM-элементов (слайдер) */
|
||||
Object.entries({main, next, previous}).forEach(([name, value]) => {
|
||||
if (value instanceof HTMLElement)
|
||||
self[name] = value
|
||||
else throw new Error(`Невалидный аргумент ${value}`)
|
||||
})
|
||||
|
||||
/** Инициалиазация кнопок */
|
||||
Object.entries({
|
||||
[next_ref]: {
|
||||
element: next,
|
||||
func: this.next_slide
|
||||
},
|
||||
[previous_ref]: {
|
||||
element: previous,
|
||||
func: this.previous_slide
|
||||
}
|
||||
}).forEach(
|
||||
([ref, {element, func}]) => {
|
||||
element.src = ref
|
||||
element.onclick = func.bind(self)
|
||||
}
|
||||
)
|
||||
|
||||
self.current_slide = 0
|
||||
self.slides = []
|
||||
|
||||
/** Инициализация фыфок (з****лся эти JSDoc'и писать)*/
|
||||
if(Array.isArray(images))
|
||||
images.forEach(self.add_slide)
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при инициалиазции slider-page");
|
||||
e.forEach(er => {throw er});
|
||||
}
|
||||
) (this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет слайдер (смещает на 80vw * current_slide)
|
||||
* @method
|
||||
*/
|
||||
update_slider = recovery(
|
||||
() => this.main.style.transform = `translateX(-${this.current_slide * 80}vw)`,
|
||||
(...e) => {
|
||||
console.log("Ошибка при обновлении слайдера (Slider.update_slider)")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Переключает на следующий слайд
|
||||
* @method
|
||||
*/
|
||||
next_slide = recovery(
|
||||
() => {
|
||||
if (this.current_slide < this.slides.length - 1)
|
||||
this.current_slide++
|
||||
else
|
||||
this.current_slide = 0
|
||||
this.update_slider()
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при переключении на следующий слайд")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Переключает на предыдущий слайд
|
||||
* @method
|
||||
*/
|
||||
previous_slide = recovery(
|
||||
() => {
|
||||
if (this.current_slide > 0)
|
||||
this.current_slide--
|
||||
else
|
||||
this.current_slide = this.slides.length - 1
|
||||
this.update_slider()
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при переключении на следующий слайд")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Добавление новых слайдов
|
||||
* @method
|
||||
*/
|
||||
add_slide = recovery(
|
||||
slide_ref => {
|
||||
/** Изображение */
|
||||
const img = document.createElement('img')
|
||||
/** Применение стилей */
|
||||
img.classList.add('slide')
|
||||
/** Линковка ссылки */
|
||||
img.src = slide_ref
|
||||
|
||||
/** Регистрация слайда в dom-элементе */
|
||||
this.main.appendChild(img)
|
||||
/** Регистрация в массиве */
|
||||
this.slides.push(img)
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при добавлении слайда")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
)
|
||||
|
||||
clear = recovery(
|
||||
() => {
|
||||
this.main.innerHTML = ''
|
||||
},
|
||||
(...e) => {
|
||||
console.log("Ошибка при очищении")
|
||||
e.forEach(er => {throw er})
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
:root,
|
||||
:host {
|
||||
--ol-background-color: white;
|
||||
--ol-accent-background-color: #F5F5F5;
|
||||
--ol-subtle-background-color: rgba(128, 128, 128, 0.25);
|
||||
--ol-partial-background-color: rgba(255, 255, 255, 0.75);
|
||||
--ol-foreground-color: #333333;
|
||||
--ol-subtle-foreground-color: #666666;
|
||||
--ol-brand-color: #00AAFF;
|
||||
}
|
||||
|
||||
.ol-box {
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
border: 1.5px solid var(--ol-background-color);
|
||||
background-color: var(--ol-partial-background-color);
|
||||
}
|
||||
|
||||
.ol-mouse-position {
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.ol-scale-line {
|
||||
background: var(--ol-partial-background-color);
|
||||
border-radius: 4px;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
padding: 2px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.ol-scale-line-inner {
|
||||
border: 1px solid var(--ol-subtle-foreground-color);
|
||||
border-top: none;
|
||||
color: var(--ol-foreground-color);
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
margin: 1px;
|
||||
will-change: contents, width;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.ol-scale-bar {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.ol-scale-bar-inner {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ol-scale-step-marker {
|
||||
width: 1px;
|
||||
height: 15px;
|
||||
background-color: var(--ol-foreground-color);
|
||||
float: right;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ol-scale-step-text {
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
font-size: 10px;
|
||||
z-index: 11;
|
||||
color: var(--ol-foreground-color);
|
||||
text-shadow: -1.5px 0 var(--ol-partial-background-color), 0 1.5px var(--ol-partial-background-color), 1.5px 0 var(--ol-partial-background-color), 0 -1.5px var(--ol-partial-background-color);
|
||||
}
|
||||
|
||||
.ol-scale-text {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
bottom: 25px;
|
||||
color: var(--ol-foreground-color);
|
||||
text-shadow: -1.5px 0 var(--ol-partial-background-color), 0 1.5px var(--ol-partial-background-color), 1.5px 0 var(--ol-partial-background-color), 0 -1.5px var(--ol-partial-background-color);
|
||||
}
|
||||
|
||||
.ol-scale-singlebar {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
z-index: 9;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--ol-foreground-color);
|
||||
}
|
||||
|
||||
.ol-scale-singlebar-even {
|
||||
background-color: var(--ol-subtle-foreground-color);
|
||||
}
|
||||
|
||||
.ol-scale-singlebar-odd {
|
||||
background-color: var(--ol-background-color);
|
||||
}
|
||||
|
||||
.ol-unsupported {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ol-viewport,
|
||||
.ol-unselectable {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.ol-viewport canvas {
|
||||
all: unset;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ol-viewport {
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
|
||||
.ol-selectable {
|
||||
-webkit-touch-callout: default;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.ol-grabbing {
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.ol-grab {
|
||||
cursor: move;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.ol-control {
|
||||
position: absolute;
|
||||
background-color: var(--ol-subtle-background-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ol-zoom {
|
||||
top: .5em;
|
||||
left: .5em;
|
||||
}
|
||||
|
||||
.ol-rotate {
|
||||
top: .5em;
|
||||
right: .5em;
|
||||
transition: opacity .25s linear, visibility 0s linear;
|
||||
}
|
||||
|
||||
.ol-rotate.ol-hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity .25s linear, visibility 0s linear .25s;
|
||||
}
|
||||
|
||||
.ol-zoom-extent {
|
||||
top: 4.643em;
|
||||
left: .5em;
|
||||
}
|
||||
|
||||
.ol-full-screen {
|
||||
right: .5em;
|
||||
top: .5em;
|
||||
}
|
||||
|
||||
.ol-control button {
|
||||
display: block;
|
||||
margin: 1px;
|
||||
padding: 0;
|
||||
color: var(--ol-subtle-foreground-color);
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
text-align: center;
|
||||
height: 1.375em;
|
||||
width: 1.375em;
|
||||
line-height: .4em;
|
||||
background-color: var(--ol-background-color);
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.ol-control button::-moz-focus-inner {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ol-zoom-extent button {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
.ol-compass {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.ol-touch .ol-control button {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.ol-touch .ol-zoom-extent {
|
||||
top: 5.5em;
|
||||
}
|
||||
|
||||
.ol-control button:hover,
|
||||
.ol-control button:focus {
|
||||
text-decoration: none;
|
||||
outline: 1px solid var(--ol-subtle-foreground-color);
|
||||
color: var(--ol-foreground-color);
|
||||
}
|
||||
|
||||
.ol-zoom .ol-zoom-in {
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
.ol-zoom .ol-zoom-out {
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
|
||||
.ol-attribution {
|
||||
text-align: right;
|
||||
bottom: .5em;
|
||||
right: .5em;
|
||||
max-width: calc(100% - 1.3em);
|
||||
display: flex;
|
||||
flex-flow: row-reverse;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ol-attribution a {
|
||||
color: var(--ol-subtle-foreground-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ol-attribution ul {
|
||||
margin: 0;
|
||||
padding: 1px .5em;
|
||||
color: var(--ol-foreground-color);
|
||||
text-shadow: 0 0 2px var(--ol-background-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ol-attribution li {
|
||||
display: inline;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.ol-attribution li:not(:last-child):after {
|
||||
content: " ";
|
||||
}
|
||||
|
||||
.ol-attribution img {
|
||||
max-height: 2em;
|
||||
max-width: inherit;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ol-attribution button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ol-attribution.ol-collapsed ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ol-attribution:not(.ol-collapsed) {
|
||||
background: var(--ol-partial-background-color);
|
||||
}
|
||||
|
||||
.ol-attribution.ol-uncollapsible {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 4px 0 0;
|
||||
}
|
||||
|
||||
.ol-attribution.ol-uncollapsible img {
|
||||
margin-top: -.2em;
|
||||
max-height: 1.6em;
|
||||
}
|
||||
|
||||
.ol-attribution.ol-uncollapsible button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ol-zoomslider {
|
||||
top: 4.5em;
|
||||
left: .5em;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.ol-zoomslider button {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.ol-touch .ol-zoomslider {
|
||||
top: 5.5em;
|
||||
}
|
||||
|
||||
.ol-overviewmap {
|
||||
left: 0.5em;
|
||||
bottom: 0.5em;
|
||||
}
|
||||
|
||||
.ol-overviewmap.ol-uncollapsible {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-radius: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.ol-overviewmap .ol-overviewmap-map,
|
||||
.ol-overviewmap button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ol-overviewmap .ol-overviewmap-map {
|
||||
border: 1px solid var(--ol-subtle-foreground-color);
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.ol-overviewmap:not(.ol-collapsed) button {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.ol-overviewmap.ol-collapsed .ol-overviewmap-map,
|
||||
.ol-overviewmap.ol-uncollapsible button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ol-overviewmap:not(.ol-collapsed) {
|
||||
background: var(--ol-subtle-background-color);
|
||||
}
|
||||
|
||||
.ol-overviewmap-box {
|
||||
border: 1.5px dotted var(--ol-subtle-foreground-color);
|
||||
}
|
||||
|
||||
.ol-overviewmap .ol-overviewmap-box:hover {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ol-overviewmap .ol-viewport:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
namespace Catcher;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Вызов обработчика ошибок с защитой от повторных исключений.
|
||||
*
|
||||
* @param callable $restorer Функция восстановления, принимающая исключения.
|
||||
* @param Throwable ...$errors Исключения, переданные для обработки.
|
||||
* @return mixed
|
||||
* @throws Throwable
|
||||
*/
|
||||
function start_restorer(callable $restorer, Throwable ...$errors) {
|
||||
try {
|
||||
return $restorer(...$errors);
|
||||
} catch (Throwable $e) {
|
||||
error_log("Restorer error: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Оборачивает функцию и добавляет обработку ошибок.
|
||||
*
|
||||
* @param callable $func Основная функция.
|
||||
* @param callable|null $restorer Обработчик ошибок (по умолчанию — пробрасывает исключение).
|
||||
* @return callable
|
||||
*/
|
||||
function recovery(callable $func, callable $restorer = null): callable {
|
||||
if ($restorer === null) {
|
||||
$restorer = function (Throwable ...$errors) {
|
||||
throw $errors[0]; // Пробрасываем первое исключение
|
||||
};
|
||||
}
|
||||
|
||||
return function (...$args) use ($func, $restorer) {
|
||||
try {
|
||||
return $func(...$args);
|
||||
} catch (Throwable $e) {
|
||||
return start_restorer($restorer, $e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
namespace DataBaseManager\DBManager;
|
||||
use DataBaseManager\Entitie\Entitie;
|
||||
use PDO;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class DBManager
|
||||
{
|
||||
private $pdo;
|
||||
private $entitie;
|
||||
public function __construct(Entitie $entitie, object $config)
|
||||
{
|
||||
$this->entitie = $entitie;
|
||||
$dsn = "{$config->driver}:host={$config->host};port={$config->port};dbname={$config->dbname}";
|
||||
$username = $config->username;
|
||||
$password = $config->password;
|
||||
$options = isset($config->options) ? (array)$config->options : [];
|
||||
$this->pdo = new PDO($dsn, $username, $password, $options);
|
||||
}
|
||||
public function create_table()
|
||||
{
|
||||
return $this->pdo->query($this->entitie->get_create_table());
|
||||
}
|
||||
public function delete_table()
|
||||
{
|
||||
return $this->pdo->query($this->entitie->get_delete_table());
|
||||
}
|
||||
public function create(array $params)
|
||||
{
|
||||
$stmt = $this->pdo->prepare($this->entitie->get_create());
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
public function select(array $params)
|
||||
{
|
||||
$sql = str_replace("*", $params[0], $this->entitie->get_select());
|
||||
array_shift($params);
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function select_all(array $params = ["*"])
|
||||
{
|
||||
$sql = str_replace("*", $params[0], $this->entitie->get_select_all());
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute([]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function select_by_condition(array $params)
|
||||
{
|
||||
$sql = str_replace("*", $params[0], $this->entitie->get_select_all());
|
||||
array_shift($params);
|
||||
$stmt = $this->pdo->prepare($sql . ' WHERE ' . $params[0]);
|
||||
$stmt->execute([]);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function remove(array $params)
|
||||
{
|
||||
$stmt = $this->pdo->prepare($this->entitie->get_remove());
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
public function clear(array $params)
|
||||
{
|
||||
$stmt = $this->pdo->prepare($this->entitie->get_clear());
|
||||
return $stmt->execute($params);
|
||||
}
|
||||
public function create_all(array $params)
|
||||
{
|
||||
$create_all = $this->entitie->get_create_all();
|
||||
$stmt = $this->pdo->prepare($create_all(sizeof($params)));
|
||||
$params_merge = array_merge(...$params);
|
||||
return $stmt->execute($params_merge);
|
||||
}
|
||||
public function update(array $params)
|
||||
{
|
||||
$update = $this->entitie->get_update();
|
||||
$stmt = $this->pdo->prepare($update($params[0]));
|
||||
array_shift($params);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
public function update_by_condition(array $params)
|
||||
{
|
||||
$update = $this->entitie->get_update_by_condition();
|
||||
$stmt = $this->pdo->prepare($update($params[0]));
|
||||
array_shift($params);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
namespace DataBaseManager\Entitie;
|
||||
|
||||
class Entitie
|
||||
{
|
||||
private array $column_array;
|
||||
private string $table_name;
|
||||
private string $create_table;
|
||||
private string $delete_table;
|
||||
private string $type_id;
|
||||
private string $create;
|
||||
private string $select;
|
||||
private string $select_all;
|
||||
private string $remove;
|
||||
private string $clear;
|
||||
private \Closure $create_all;
|
||||
private \Closure $update;
|
||||
private \Closure $update_by_condition;
|
||||
|
||||
public function __construct(array $column_array, string $table_name, string $type_id = "serial")
|
||||
{
|
||||
$this->column_array = $column_array;
|
||||
$this->table_name = $table_name;
|
||||
$this->type_id = $type_id;
|
||||
|
||||
$columns_sql = implode(", ", $this->column_array);
|
||||
$column_names = implode(", ", array_map(fn($col) => explode(" ", $col)[0], $this->column_array));
|
||||
$placeholders = implode(", ", array_fill(0, count($this->column_array), "?"));
|
||||
$set_clause = implode(", ", array_map(fn($col) => explode(" ", $col)[0] . "=?", $this->column_array));
|
||||
|
||||
$this->create_table = "CREATE TABLE IF NOT EXISTS {$this->table_name} (id {$this->type_id}, {$columns_sql})";
|
||||
$this->delete_table = "DROP TABLE IF EXISTS {$this->table_name}";
|
||||
$this->create = "INSERT INTO {$this->table_name} ({$column_names}) VALUES ({$placeholders})";
|
||||
$this->select = "SELECT * FROM {$this->table_name} WHERE id=?";
|
||||
$this->select_all = "SELECT * FROM {$this->table_name}";
|
||||
$this->remove = "DELETE FROM {$this->table_name} WHERE id=?";
|
||||
$this->clear = "TRUNCATE TABLE {$this->table_name}";
|
||||
|
||||
$this->create_all = function($length) use ($placeholders, $column_names, $table_name) {
|
||||
$placeholders_array = implode(", ", array_fill(0, $length, "({$placeholders})"));
|
||||
return "INSERT INTO {$table_name} ({$column_names}) VALUES {$placeholders_array}";
|
||||
};
|
||||
$this->update = fn($id) => "UPDATE {$this->table_name} SET {$set_clause} WHERE id = ?";
|
||||
$this->update_by_condition = fn($condition) => "UPDATE {$this->table_name} SET {$set_clause} WHERE {$condition}";
|
||||
}
|
||||
|
||||
public function get_create_table(): string { return $this->create_table; }
|
||||
public function get_delete_table(): string { return $this->delete_table; }
|
||||
public function get_create(): string { return $this->create; }
|
||||
public function get_select(): string { return $this->select; }
|
||||
public function get_select_all(): string { return $this->select_all; }
|
||||
public function get_remove(): string { return $this->remove; }
|
||||
public function get_clear(): string { return $this->clear; }
|
||||
public function get_create_all(): \Closure { return $this->create_all; }
|
||||
public function get_update(): \Closure { return $this->update; }
|
||||
public function get_update_by_condition(): \Closure { return $this->update_by_condition; }
|
||||
}
|
||||