tmp_teh_zadanie_2_23.07.26_18:04
Различия
Показаны различия между двумя версиями страницы.
| Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
| tmp_teh_zadanie_2_23.07.26_18:04 [2026/07/23 18:17] – [local.php] VladPolskiy | tmp_teh_zadanie_2_23.07.26_18:04 [2026/07/23 18:36] (текущий) – [local.php] VladPolskiy | ||
|---|---|---|---|
| Строка 984: | Строка 984: | ||
| </ | </ | ||
| - | ===== local.php ===== | + | ===== users.auth.php ===== |
| - | <code php local.php> | + | <code php users.auth.php> |
| + | <?php die(); ?> | ||
| + | # MicroWiki Users Auth File | ||
| + | # users.auth.php | ||
| + | # Формат записи: | ||
| + | admin: | ||
| + | editor: | ||
| </ | </ | ||
| + | ===== hash.php ===== | ||
| + | <code php hash.php> | ||
| + | <?php | ||
| + | echo password_hash(' | ||
| + | </ | ||
| + | |||
| + | ===== storage.php ===== | ||
| + | <code php storage.php> | ||
| + | <?php | ||
| + | /** | ||
| + | * Класс управления плоскими файлами страниц (Аналог файлового ядра DokuWiki) | ||
| + | * storage.php | ||
| + | */ | ||
| + | |||
| + | declare(strict_types=1); | ||
| + | |||
| + | if (!defined(' | ||
| + | die(' | ||
| + | } | ||
| + | |||
| + | class Storage { | ||
| + | private string $baseDir; | ||
| + | |||
| + | /** | ||
| + | * Конструктор инициализирует базовую директорию для хранения текстов | ||
| + | */ | ||
| + | public function __construct(string $baseDataPath) { | ||
| + | // Гарантируем правильные слеши в путях для Windows/ | ||
| + | $this-> | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Преобразует Wiki ID (например, | ||
| + | */ | ||
| + | public function resolvePath(string $id): string { | ||
| + | // Безопасность: | ||
| + | $id = str_replace([' | ||
| + | | ||
| + | // В DokuWiki двоеточие — это разделитель папок. Меняем ":" | ||
| + | $relativePath = str_replace(':', | ||
| + | | ||
| + | return $this-> | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Проверяет, | ||
| + | */ | ||
| + | public function exists(string $id): bool { | ||
| + | return file_exists($this-> | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Безопасно читает содержимое вики-страницы | ||
| + | */ | ||
| + | public function read(string $id): string { | ||
| + | $filePath = $this-> | ||
| + | if (file_exists($filePath)) { | ||
| + | return file_get_contents($filePath) ?: ''; | ||
| + | } | ||
| + | return ''; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Сохраняет текст страницы, | ||
| + | */ | ||
| + | public function save(string $id, string $content): bool { | ||
| + | $filePath = $this-> | ||
| + | $dirPath | ||
| + | |||
| + | // Если папки для пространства имен не существуют, | ||
| + | if (!is_dir($dirPath)) { | ||
| + | if (!mkdir($dirPath, | ||
| + | return false; // Не удалось создать папку (проблема прав доступа) | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Записываем данные в файл атомарно | ||
| + | return file_put_contents($filePath, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== MediaManager.php ===== | ||
| + | <code php MediaManager.php> | ||
| + | <?php | ||
| + | /** | ||
| + | * Класс управления медиафайлами и изображениями (Аналог подсистемы Media в DokuWiki) | ||
| + | */ | ||
| + | |||
| + | declare(strict_types=1); | ||
| + | |||
| + | if (!defined(' | ||
| + | die(' | ||
| + | } | ||
| + | |||
| + | class MediaManager { | ||
| + | private string $baseMediaDir; | ||
| + | private array $allowedTypes; | ||
| + | |||
| + | public function __construct(string $baseDataPath) { | ||
| + | $this-> | ||
| + | // Белый список разрешенных расширений файлов | ||
| + | $this-> | ||
| + | ' | ||
| + | ]; | ||
| + | } | ||
| + | |||
| + | public function resolveDir(string $ns): string { | ||
| + | $ns = str_replace([' | ||
| + | $relativePath = str_replace(':', | ||
| + | return rtrim($this-> | ||
| + | } | ||
| + | |||
| + | public function getFiles(string $ns): array { | ||
| + | $dir = $this-> | ||
| + | if (!is_dir($dir)) { | ||
| + | return []; | ||
| + | } | ||
| + | |||
| + | $result = []; | ||
| + | $files = scandir($dir); | ||
| + | | ||
| + | foreach ($files as $file) { | ||
| + | if ($file === ' | ||
| + | continue; | ||
| + | } | ||
| + | if (is_file($dir . $file)) { | ||
| + | $ext = strtolower(pathinfo($file, | ||
| + | $isImage = in_array($ext, | ||
| + | | ||
| + | $result[] = [ | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ]; | ||
| + | } | ||
| + | } | ||
| + | return $result; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Безопасная обработка и загрузка файла из глобального массива $_FILES | ||
| + | */ | ||
| + | public function upload(string $ns): bool { | ||
| + | if (!isset($_FILES[' | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | $error = $_FILES[' | ||
| + | if ($error !== UPLOAD_ERR_OK) { | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | $name = $_FILES[' | ||
| + | $tmp_name = $_FILES[' | ||
| + | |||
| + | // Очищаем имя файла от опасных символов | ||
| + | $filename = preg_replace('/ | ||
| + | $ext = strtolower(pathinfo($filename, | ||
| + | |||
| + | // Жесткая проверка: | ||
| + | if (!in_array($ext, | ||
| + | return false; | ||
| + | } | ||
| + | |||
| + | $targetDir = $this-> | ||
| + | if (!is_dir($targetDir)) { | ||
| + | mkdir($targetDir, | ||
| + | } | ||
| + | |||
| + | // Переносим файл из временной папки сервера в каталог data/media/ | ||
| + | return move_uploaded_file($tmp_name, | ||
| + | } | ||
| + | |||
| + | public function delete(string $ns, string $filename): bool { | ||
| + | $filename = preg_replace('/ | ||
| + | $filePath = $this-> | ||
| + | | ||
| + | if (file_exists($filePath) && is_file($filePath)) { | ||
| + | return unlink($filePath); | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== Parser.php ===== | ||
| + | <code php Parser.php> | ||
| + | <?php | ||
| + | /** | ||
| + | * Класс трансляции вики-разметки в HTML (Аналог подсистемы парсера DokuWiki) | ||
| + | */ | ||
| + | |||
| + | declare(strict_types=1); | ||
| + | |||
| + | if (!defined(' | ||
| + | die(' | ||
| + | } | ||
| + | |||
| + | class Parser { | ||
| + | public function render(string $text): string { | ||
| + | require_once WIKI_ROOT . ' | ||
| + | $highlighter = new CodeHighlighter(); | ||
| + | |||
| + | $codeBlocks = []; | ||
| + | $blockCount = 0; | ||
| + | |||
| + | // Шаг 1. Вырезаем блоки < | ||
| + | $text = preg_replace_callback('/< | ||
| + | $paramLine = isset($matches[1]) ? (string)$matches[1] : ''; | ||
| + | $codeBody | ||
| + | |||
| + | // Обрабатываем блок кода в чистом виде | ||
| + | $renderedBlock = $highlighter-> | ||
| + | | ||
| + | // Исправлено: | ||
| + | $marker = " | ||
| + | $codeBlocks[$marker] = $renderedBlock; | ||
| + | $blockCount++; | ||
| + | |||
| + | return $marker; | ||
| + | }, $text); | ||
| + | |||
| + | // Шаг 2. Безопасность: | ||
| + | $text = htmlspecialchars($text, | ||
| + | |||
| + | // Шаг 3. Парсим заголовки DokuWiki | ||
| + | $text = preg_replace('/ | ||
| + | $text = preg_replace('/ | ||
| + | $text = preg_replace('/ | ||
| + | |||
| + | // Шаг 4. Стили форматирования текста | ||
| + | $text = preg_replace('/ | ||
| + | $text = preg_replace('/ | ||
| + | $text = preg_replace('/ | ||
| + | |||
| + | // Шаг 5. Парсим ссылки [[page_id]] или [[page_id|Текст]] | ||
| + | $text = preg_replace_callback('/ | ||
| + | $innerContent = isset($matches[1]) ? (string)$matches[1] : ''; | ||
| + | if (str_contains($innerContent, | ||
| + | $parts = explode(' | ||
| + | $pageId = trim((string)array_shift($parts)); | ||
| + | $label | ||
| + | } else { | ||
| + | $pageId = trim($innerContent); | ||
| + | $label | ||
| + | } | ||
| + | $pageId = ltrim($pageId, | ||
| + | return '<a href="? | ||
| + | }, $text); | ||
| + | |||
| + | // Шаг 6. Парсим медиа-теги изображений {{: | ||
| + | $text = preg_replace_callback('/ | ||
| + | $innerMedia = isset($matches[1]) ? (string)$matches[1] : ''; | ||
| + | if (str_contains($innerMedia, | ||
| + | $mediaParts = explode(' | ||
| + | $mediaPath = trim((string)array_shift($mediaParts)); | ||
| + | } else { | ||
| + | $mediaPath = trim($innerMedia); | ||
| + | } | ||
| + | $realPath = str_replace(':', | ||
| + | return '< | ||
| + | }, $text); | ||
| + | |||
| + | // Шаг 7. Делаем автоматические переносы строк для текста страницы | ||
| + | $text = nl2br($text); | ||
| + | |||
| + | // Шаг 8. Финал: возвращаем сохраненные блоки кода обратно на свои места в обход nl2br | ||
| + | foreach ($codeBlocks as $marker => $renderedHtml) { | ||
| + | $text = str_replace($marker, | ||
| + | } | ||
| + | |||
| + | return $text; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== CodeHighlighter.php ===== | ||
| + | <code php CodeHighlighter.php> | ||
| + | <?php | ||
| + | /** | ||
| + | * Системный компонент: | ||
| + | */ | ||
| + | |||
| + | declare(strict_types=1); | ||
| + | |||
| + | if (!defined(' | ||
| + | die(' | ||
| + | } | ||
| + | |||
| + | class CodeHighlighter { | ||
| + | public function highlight(string $paramLine, string $code): string { | ||
| + | $code = htmlspecialchars_decode($code, | ||
| + | $code = trim($code, " | ||
| + | $code = trim($code); | ||
| + | | ||
| + | $paramLine = htmlspecialchars_decode($paramLine, | ||
| + | $paramLine = trim($paramLine); | ||
| + | $parts = explode(' | ||
| + | | ||
| + | $firstPart = array_shift($parts); | ||
| + | $lang = $firstPart !== null ? strtolower(trim((string)$firstPart)) : ' | ||
| + | | ||
| + | $filename = ''; | ||
| + | $highlightLine = 0; | ||
| + | $startLine = 1; | ||
| + | |||
| + | // ЖЕСТКОЕ ИСПРАВЛЕНО: | ||
| + | if (preg_match('/" | ||
| + | $filename = isset($fileMatches[' | ||
| + | | ||
| + | // Вырезаем кавычки с файлом из параметров | ||
| + | $paramLine = preg_replace('/" | ||
| + | $parts = explode(' | ||
| + | } | ||
| + | |||
| + | foreach ($parts as $part) { | ||
| + | $part = trim((string)$part); | ||
| + | if (empty($part)) continue; | ||
| + | | ||
| + | if (str_contains($part, | ||
| + | $kv = explode(' | ||
| + | $key = isset($kv[0]) ? trim((string)$kv[0]) : ''; | ||
| + | $val = isset($kv[1]) ? trim((string)$kv[1]) : ''; | ||
| + | | ||
| + | if ($key === ' | ||
| + | if ($key === ' | ||
| + | } elseif (empty($filename) && $part !== $lang) { | ||
| + | $filename = $part; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | $lines = preg_split('/ | ||
| + | if ($lines === false) { $lines = [$code]; } | ||
| + | | ||
| + | $firstCodeLineIdx = -1; | ||
| + | foreach ($lines as $idx => $line) { | ||
| + | $t = trim($line); | ||
| + | if (!empty($t) && !str_starts_with($t, | ||
| + | $firstCodeLineIdx = $idx; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | $htmlLines = ''; | ||
| + | $currentLineNum = $startLine; | ||
| + | |||
| + | foreach ($lines as $idx => $line) { | ||
| + | $trimmedLine = trim($line); | ||
| + | $isCommentStar = str_starts_with($trimmedLine, | ||
| + | |||
| + | $colorLine = $this-> | ||
| + | $highlightClass = ($currentLineNum === $highlightLine) ? ' class=" | ||
| + | | ||
| + | $htmlLines .= '< | ||
| + | $htmlLines .= '< | ||
| + | $htmlLines .= '< | ||
| + | $htmlLines .= '</ | ||
| + | | ||
| + | $currentLineNum++; | ||
| + | } | ||
| + | |||
| + | $html = '< | ||
| + | if (!empty($filename)) { | ||
| + | $html .= '< | ||
| + | $html .= '< | ||
| + | $html .= htmlspecialchars($filename); | ||
| + | $html .= '</ | ||
| + | } | ||
| + | | ||
| + | $html .= '< | ||
| + | $html .= '</ | ||
| + | |||
| + | return $html; | ||
| + | } | ||
| + | |||
| + | private function colorizeLine(string $line, string $lang, bool $isCommentStar): | ||
| + | $line = htmlspecialchars($line, | ||
| + | |||
| + | if ($lang === ' | ||
| + | if ($isCommentStar) { | ||
| + | return '< | ||
| + | } | ||
| + | |||
| + | $commentPart = ''; | ||
| + | if (preg_match('/ | ||
| + | $commentPart = isset($matches[0]) ? (string)$matches[0] : ''; | ||
| + | $line = str_replace($commentPart, | ||
| + | } | ||
| + | |||
| + | $line = preg_replace_callback('/ | ||
| + | return '< | ||
| + | }, $line); | ||
| + | |||
| + | if ($lang === ' | ||
| + | $line = preg_replace_callback('/ | ||
| + | return '< | ||
| + | }, $line); | ||
| + | | ||
| + | $controlKeywords = [' | ||
| + | foreach ($controlKeywords as $word) { | ||
| + | $line = preg_replace_callback('/ | ||
| + | return '< | ||
| + | }, $line); | ||
| + | } | ||
| + | |||
| + | $funcKeywords = [' | ||
| + | foreach ($funcKeywords as $word) { | ||
| + | $line = preg_replace_callback('/ | ||
| + | return '< | ||
| + | }, $line); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Обычная замена индексов массивов на безопасный вывод в скобках | ||
| + | $line = preg_replace_callback('/ | ||
| + | return '< | ||
| + | }, $line); | ||
| + | |||
| + | if (!empty($commentPart)) { | ||
| + | $cleanComment = strip_tags($commentPart); | ||
| + | $commentHtml = '< | ||
| + | $line = str_replace(' | ||
| + | } | ||
| + | } | ||
| + | |||
| + | return $line; | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== toolbar.php ===== | ||
| + | <file html toolbar.php> | ||
| + | <?php | ||
| + | /** | ||
| + | * Системный компонент редактора: | ||
| + | */ | ||
| + | if (!defined(' | ||
| + | ?> | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <!-- Текст --> | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | </ | ||
| + | |||
| + | <!-- ВЫПАДАЮЩЕЕ МЕНЮ ЗАГОЛОВКОВ (ГОРИЗОНТАЛЬНЫЙ ПИКЕР) --> | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <button type=" | ||
| + | < | ||
| + | </ | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <!-- Ластик / Очистка стиля --> | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | <!-- Кнопки уровней H1-H5 --> | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | <button type=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <div class=" | ||
| + | <!-- Списки и таблицы --> | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | | ||
| + | <!-- ИСПРАВЛЕНО: | ||
| + | <div class=" | ||
| + | <button type=" | ||
| + | & | ||
| + | </ | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <!-- Обычный код --> | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | <!-- HTML --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | <!-- PHP --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | <!-- CSS --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | <!-- JS --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | <!-- SQL --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | <!-- BASH --> | ||
| + | <button type=" | ||
| + | <div class=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <div class=" | ||
| + | <!-- Ссылки --> | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | <button type=" | ||
| + | <svg width=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <!-- Каркас модального окна скрыт ниже, без изменений --> | ||
| + | <div id=" | ||
| + | <div style=" | ||
| + | <span onclick=" | ||
| + | <h3 style=" | ||
| + | <div id=" | ||
| + | <div style=" | ||
| + | <input type=" | ||
| + | <span id=" | ||
| + | </ | ||
| + | <div style=" | ||
| + | <span onclick=" | ||
| + | <span onclick=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | ===== toolbar.js ===== | ||
| + | <code js toolbar.js> | ||
| + | /** | ||
| + | * Функция вставки вики-тегов в поле редактора | ||
| + | */ | ||
| + | function insertTags(openTag, | ||
| + | var txtarea = document.getElementById(" | ||
| + | if (!txtarea) return; | ||
| + | |||
| + | var start = txtarea.selectionStart; | ||
| + | var end = txtarea.selectionEnd; | ||
| + | var selText = txtarea.value.substring(start, | ||
| + | | ||
| + | if (!selText) { | ||
| + | selText = defaultText; | ||
| + | } | ||
| + | | ||
| + | var replacement = ''; | ||
| + | var newCursorStart = start; | ||
| + | var newCursorEnd = end; | ||
| + | |||
| + | if (openTag.indexOf(' | ||
| + | replacement = openTag.replace(' | ||
| + | txtarea.value = txtarea.value.substring(0, | ||
| + | newCursorStart = start + 3; | ||
| + | newCursorEnd = start + 3; | ||
| + | } else { | ||
| + | replacement = openTag + selText + closeTag; | ||
| + | txtarea.value = txtarea.value.substring(0, | ||
| + | newCursorStart = start; | ||
| + | newCursorEnd = start + replacement.length; | ||
| + | } | ||
| + | | ||
| + | txtarea.focus(); | ||
| + | txtarea.selectionStart = newCursorStart; | ||
| + | txtarea.selectionEnd = newCursorEnd; | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Инициализация горячих клавиш | ||
| + | */ | ||
| + | document.addEventListener(" | ||
| + | var editor = document.getElementById(" | ||
| + | if (!editor) return; | ||
| + | |||
| + | editor.addEventListener(" | ||
| + | var isCtrl = e.ctrlKey || e.metaKey; | ||
| + | if (isCtrl) { | ||
| + | if (e.key.toLowerCase() === ' | ||
| + | if (e.key.toLowerCase() === ' | ||
| + | if (e.key.toLowerCase() === ' | ||
| + | } | ||
| + | }); | ||
| + | }); | ||
| + | |||
| + | /** | ||
| + | * Асинхронный движок предпросмотра текста в стиле DokuWiki | ||
| + | */ | ||
| + | function runLivePreview() { | ||
| + | var editor = document.getElementById(" | ||
| + | var previewBlock = document.getElementById(" | ||
| + | var previewContent = document.getElementById(" | ||
| + | | ||
| + | if (!editor || !previewBlock || !previewContent) return; | ||
| + | | ||
| + | var textValue = editor.value; | ||
| + | | ||
| + | var formData = new FormData(); | ||
| + | formData.append(" | ||
| + | | ||
| + | var urlParams = new URLSearchParams(window.location.search); | ||
| + | var pageId = urlParams.get(' | ||
| + | | ||
| + | previewContent.innerHTML = "< | ||
| + | previewBlock.style.display = " | ||
| + | | ||
| + | previewBlock.scrollIntoView({ behavior: ' | ||
| + | |||
| + | // Отправляем запрос на бэкенд | ||
| + | fetch('? | ||
| + | method: ' | ||
| + | body: formData | ||
| + | }) | ||
| + | .then(function(response) { | ||
| + | if (!response.ok) { | ||
| + | throw new Error(" | ||
| + | } | ||
| + | // ИСПРАВЛЕНО: | ||
| + | return response.text(); | ||
| + | }) | ||
| + | .then(function(htmlResult) { | ||
| + | // Мгновенно отрисовываем HTML в блоке превью | ||
| + | previewContent.innerHTML = htmlResult; | ||
| + | }) | ||
| + | .catch(function(error) { | ||
| + | console.error(' | ||
| + | previewContent.innerHTML = "< | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | // Добавьте этот код в самый конец файла assets/ | ||
| + | |||
| + | /** | ||
| + | * Управление выпадающими горизонтальными пикерами Toolbar | ||
| + | */ | ||
| + | document.addEventListener(" | ||
| + | var btnH = document.getElementById(" | ||
| + | var btnCode = document.getElementById(" | ||
| + | | ||
| + | var dropH = document.getElementById(" | ||
| + | var dropCode = document.getElementById(" | ||
| + | |||
| + | if (btnH && dropH) { | ||
| + | btnH.addEventListener(" | ||
| + | e.stopPropagation(); | ||
| + | if (dropCode) dropCode.classList.remove(" | ||
| + | dropH.classList.toggle(" | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | if (btnCode && dropCode) { | ||
| + | btnCode.addEventListener(" | ||
| + | e.stopPropagation(); | ||
| + | if (dropH) dropH.classList.remove(" | ||
| + | dropCode.classList.toggle(" | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | document.addEventListener(" | ||
| + | if (dropH) dropH.classList.remove(" | ||
| + | if (dropCode) dropCode.classList.remove(" | ||
| + | }); | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | ===== media-modal.js ===== | ||
| + | <code js media-modal.js> | ||
| + | /** | ||
| + | * Системный компонент: | ||
| + | */ | ||
| + | |||
| + | function openMediaModal() { | ||
| + | var modal = document.getElementById(" | ||
| + | if (modal) { | ||
| + | modal.style.display = " | ||
| + | document.getElementById(" | ||
| + | document.getElementById(" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | function closeMediaModal() { | ||
| + | var modal = document.getElementById(" | ||
| + | if (modal) modal.style.display = " | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Асинхронная отправка файла на бэкенд без перезагрузки страницы | ||
| + | */ | ||
| + | function sendModalFile(event) { | ||
| + | // Жестко глушим любые системные события браузера, | ||
| + | if (event) { | ||
| + | event.preventDefault(); | ||
| + | event.stopPropagation(); | ||
| + | } | ||
| + | |||
| + | var fileInp = document.getElementById(" | ||
| + | if (!fileInp || !fileInp.files || fileInp.files.length === 0) { | ||
| + | alert(" | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | var urlParams = new URLSearchParams(window.location.search); | ||
| + | var pageId = urlParams.get(' | ||
| + | |||
| + | var targetFile = fileInp.files.item(0); | ||
| + | |||
| + | var formData = new FormData(); | ||
| + | formData.append(" | ||
| + | |||
| + | // Выполняем асинхронный фоновый запрос к бэкенду | ||
| + | fetch('? | ||
| + | method: ' | ||
| + | body: formData | ||
| + | }) | ||
| + | .then(function(response) { | ||
| + | if (!response.ok) { | ||
| + | throw new Error(" | ||
| + | } | ||
| + | return response.json(); | ||
| + | }) | ||
| + | .then(function(data) { | ||
| + | if (data.success) { | ||
| + | // Вставляем полученный тег в поле редактора | ||
| + | insertTags(data.tag, | ||
| + | closeMediaModal(); | ||
| + | } else { | ||
| + | alert(" | ||
| + | } | ||
| + | }) | ||
| + | .catch(function(error) { | ||
| + | console.error(' | ||
| + | alert(" | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | document.addEventListener(" | ||
| + | var fileInp = document.getElementById(" | ||
| + | if (fileInp) { | ||
| + | fileInp.addEventListener(" | ||
| + | if (this.files && this.files.length > 0) { | ||
| + | var currentFile = this.files.item(0); | ||
| + | document.getElementById(" | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | }); | ||
| + | </ | ||
tmp_teh_zadanie_2_23.07.26_18/04.1784819846.txt.gz · Последнее изменение: — VladPolskiy
